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)
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
if response[0] == 'ref':
741
if response[0] == b'ref':
742
return response[1].decode('utf-8')
307
762
medium._remember_remote_is_before(required_version)
310
if verb == 'BzrDir.open_branch':
311
if response[0] != 'ok':
765
if verb == b'BzrDir.open_branch':
766
if response[0] != b'ok':
312
767
raise errors.UnexpectedSmartServerResponse(response)
313
if response[1] != '':
314
return ('ref', response[1])
768
if response[1] != b'':
769
return (b'ref', response[1])
316
return ('branch', '')
317
if response[0] not in ('ref', 'branch'):
771
return (b'branch', b'')
772
if response[0] not in (b'ref', b'branch'):
318
773
raise errors.UnexpectedSmartServerResponse(response)
321
def _get_tree_branch(self):
776
def _get_tree_branch(self, name=None):
322
777
"""See BzrDir._get_tree_branch()."""
323
return None, self.open_branch()
778
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':
780
def _open_branch(self, name, kind, location_or_format,
781
ignore_fallbacks=False, possible_transports=None):
336
783
# a branch reference, use the existing BranchReference logic.
337
784
format = BranchReferenceFormat()
338
785
return format.open(self, name=name, _found=True,
339
location=response[1], ignore_fallbacks=ignore_fallbacks)
340
branch_format_name = response[1]
786
location=location_or_format, ignore_fallbacks=ignore_fallbacks,
787
possible_transports=possible_transports)
788
branch_format_name = location_or_format
341
789
if not branch_format_name:
342
790
branch_format_name = None
343
791
format = RemoteBranchFormat(network_name=branch_format_name)
344
792
return RemoteBranch(self, self.find_repository(), format=format,
345
setup_stacking=not ignore_fallbacks, name=name)
793
setup_stacking=not ignore_fallbacks, name=name,
794
possible_transports=possible_transports)
796
def open_branch(self, name=None, unsupported=False,
797
ignore_fallbacks=False, possible_transports=None):
799
name = self._get_selected_branch()
801
raise errors.NoColocatedBranchSupport(self)
803
raise NotImplementedError('unsupported flag support not implemented yet.')
804
if self._next_open_branch_result is not None:
805
# See create_branch for details.
806
result = self._next_open_branch_result
807
self._next_open_branch_result = None
809
response = self._get_branch_reference()
810
return self._open_branch(name, response[0], response[1],
811
possible_transports=possible_transports,
812
ignore_fallbacks=ignore_fallbacks)
347
814
def _open_repo_v1(self, path):
348
verb = 'BzrDir.find_repository'
815
verb = b'BzrDir.find_repository'
349
816
response = self._call(verb, path)
350
if response[0] != 'ok':
817
if response[0] != b'ok':
351
818
raise errors.UnexpectedSmartServerResponse(response)
352
819
# servers that only support the v1 method don't support external
353
820
# references either.
354
821
self._ensure_real()
355
822
repo = self._real_bzrdir.open_repository()
356
response = response + ('no', repo._format.network_name())
823
response = response + (b'no', repo._format.network_name())
357
824
return response, repo
359
826
def _open_repo_v2(self, path):
360
verb = 'BzrDir.find_repositoryV2'
827
verb = b'BzrDir.find_repositoryV2'
361
828
response = self._call(verb, path)
362
if response[0] != 'ok':
829
if response[0] != b'ok':
363
830
raise errors.UnexpectedSmartServerResponse(response)
364
831
self._ensure_real()
365
832
repo = self._real_bzrdir.open_repository()
441
923
"""Upgrading of remote bzrdirs is not supported yet."""
444
def needs_format_conversion(self, format=None):
926
def needs_format_conversion(self, format):
445
927
"""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
930
def _get_config(self):
458
931
return RemoteBzrDirConfig(self)
461
class RemoteRepositoryFormat(repository.RepositoryFormat):
933
def _get_config_store(self):
934
return RemoteControlStore(self)
937
class RemoteInventoryTree(InventoryRevisionTree):
939
def __init__(self, repository, inv, revision_id):
940
super(RemoteInventoryTree, self).__init__(repository, inv, revision_id)
942
def archive(self, format, name, root=None, subdir=None, force_mtime=None):
943
ret = self._repository._revision_archive(
944
self.get_revision_id(), format, name, root, subdir,
945
force_mtime=force_mtime)
947
return super(RemoteInventoryTree, self).archive(
948
format, name, root, subdir, force_mtime=force_mtime)
951
def annotate_iter(self, path, file_id=None,
952
default_revision=_mod_revision.CURRENT_REVISION):
953
"""Return an iterator of revision_id, line tuples.
955
For working trees (and mutable trees in general), the special
956
revision_id 'current:' will be used for lines that are new in this
957
tree, e.g. uncommitted changes.
958
:param file_id: The file to produce an annotated version from
959
:param default_revision: For lines that don't match a basis, mark them
960
with this revision id. Not all implementations will make use of
963
ret = self._repository._annotate_file_revision(
964
self.get_revision_id(), path, file_id, default_revision)
966
return super(RemoteInventoryTree, self).annotate_iter(
967
path, file_id, default_revision=default_revision)
971
class RemoteRepositoryFormat(vf_repository.VersionedFileRepositoryFormat):
462
972
"""Format for repositories accessed over a _SmartClient.
464
974
Instances of this repository are represented by RemoteRepository
529
1061
self._custom_format.supports_tree_reference
530
1062
return self._supports_tree_reference
532
def _vfs_initialize(self, a_bzrdir, shared):
1065
def revision_graph_can_have_wrong_parents(self):
1066
if self._revision_graph_can_have_wrong_parents is None:
1068
self._revision_graph_can_have_wrong_parents = \
1069
self._custom_format.revision_graph_can_have_wrong_parents
1070
return self._revision_graph_can_have_wrong_parents
1072
def _vfs_initialize(self, a_controldir, shared):
533
1073
"""Helper for common code in initialize."""
534
1074
if self._custom_format:
535
1075
# Custom format requested
536
result = self._custom_format.initialize(a_bzrdir, shared=shared)
1076
result = self._custom_format.initialize(a_controldir, shared=shared)
537
1077
elif self._creating_bzrdir is not None:
538
1078
# Use the format that the repository we were created to back
540
1080
prior_repo = self._creating_bzrdir.open_repository()
541
1081
prior_repo._ensure_real()
542
1082
result = prior_repo._real_repository._format.initialize(
543
a_bzrdir, shared=shared)
1083
a_controldir, shared=shared)
545
1085
# assume that a_bzr is a RemoteBzrDir but the smart server didn't
546
1086
# support remote initialization.
547
1087
# We delegate to a real object at this point (as RemoteBzrDir
548
1088
# 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)
1089
# recursion if we just called a_controldir.create_repository.
1090
a_controldir._ensure_real()
1091
result = a_controldir._real_bzrdir.create_repository(shared=shared)
552
1092
if not isinstance(result, RemoteRepository):
553
return self.open(a_bzrdir)
1093
return self.open(a_controldir)
557
def initialize(self, a_bzrdir, shared=False):
1097
def initialize(self, a_controldir, shared=False):
558
1098
# 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
1099
if not isinstance(a_controldir, RemoteBzrDir):
1100
return self._vfs_initialize(a_controldir, shared)
1101
medium = a_controldir._client._medium
562
1102
if medium._is_remote_before((1, 13)):
563
return self._vfs_initialize(a_bzrdir, shared)
1103
return self._vfs_initialize(a_controldir, shared)
564
1104
# Creating on a remote bzr dir.
565
1105
# 1) get the network name to use.
566
1106
if self._custom_format:
568
1108
elif self._network_name:
569
1109
network_name = self._network_name
571
# Select the current bzrlib default and ask for that.
572
reference_bzrdir_format = bzrdir.format_registry.get('default')()
1111
# Select the current breezy default and ask for that.
1112
reference_bzrdir_format = controldir.format_registry.get('default')()
573
1113
reference_format = reference_bzrdir_format.repository_format
574
1114
network_name = reference_format.network_name()
575
1115
# 2) try direct creation via RPC
576
path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
577
verb = 'BzrDir.create_repository'
1116
path = a_controldir._path_for_remote_call(a_controldir._client)
1117
verb = b'BzrDir.create_repository'
1119
shared_str = b'True'
1121
shared_str = b'False'
583
response = a_bzrdir._call(verb, path, network_name, shared_str)
1123
response = a_controldir._call(verb, path, network_name, shared_str)
584
1124
except errors.UnknownSmartMethod:
585
1125
# Fallback - use vfs methods
586
1126
medium._remember_remote_is_before((1, 13))
587
return self._vfs_initialize(a_bzrdir, shared)
1127
return self._vfs_initialize(a_controldir, shared)
589
1129
# Turn the response into a RemoteRepository object.
590
1130
format = response_tuple_to_repo_format(response[1:])
591
1131
# Used to support creating a real format instance when needed.
592
format._creating_bzrdir = a_bzrdir
593
remote_repo = RemoteRepository(a_bzrdir, format)
1132
format._creating_bzrdir = a_controldir
1133
remote_repo = RemoteRepository(a_controldir, format)
594
1134
format._creating_repo = remote_repo
595
1135
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()
1137
def open(self, a_controldir):
1138
if not isinstance(a_controldir, RemoteBzrDir):
1139
raise AssertionError('%r is not a RemoteBzrDir' % (a_controldir,))
1140
return a_controldir.open_repository()
602
1142
def _ensure_real(self):
603
1143
if self._custom_format is None:
604
self._custom_format = repository.network_format_registry.get(
1145
self._custom_format = _mod_repository.network_format_registry.get(
1148
raise errors.UnknownFormatError(kind='repository',
1149
format=self._network_name)
608
1152
def _fetch_order(self):
910
1502
# one; unfortunately the tests rely on slightly different behaviour at
911
1503
# present -- mbp 20090710
912
1504
return (self.__class__ is other.__class__ and
913
self.bzrdir.transport.base == other.bzrdir.transport.base)
1505
self.controldir.transport.base == other.controldir.transport.base)
915
1507
def get_graph(self, other_repository=None):
916
1508
"""Return the graph for this repository format"""
917
1509
parents_provider = self._make_parents_provider(other_repository)
918
1510
return graph.Graph(parents_provider)
921
1512
def get_known_graph_ancestry(self, revision_ids):
922
1513
"""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)
1515
with self.lock_read():
1516
revision_graph = dict(((key, value) for key, value in
1517
self.get_graph().iter_ancestry(revision_ids) if value is not None))
1518
revision_graph = _mod_repository._strip_NULL_ghosts(revision_graph)
1519
return graph.KnownGraph(revision_graph)
929
1521
def gather_stats(self, revid=None, committers=None):
930
1522
"""See Repository.gather_stats()."""
931
path = self.bzrdir._path_for_remote_call(self._client)
1523
path = self.controldir._path_for_remote_call(self._client)
932
1524
# revid can be None to indicate no revisions, not just NULL_REVISION
933
if revid is None or revision.is_null(revid):
1525
if revid is None or _mod_revision.is_null(revid):
936
1528
fmt_revid = revid
937
1529
if committers is None or not committers:
938
fmt_committers = 'no'
1530
fmt_committers = b'no'
940
fmt_committers = 'yes'
1532
fmt_committers = b'yes'
941
1533
response_tuple, response_handler = self._call_expecting_body(
942
'Repository.gather_stats', path, fmt_revid, fmt_committers)
943
if response_tuple[0] != 'ok':
1534
b'Repository.gather_stats', path, fmt_revid, fmt_committers)
1535
if response_tuple[0] != b'ok':
944
1536
raise errors.UnexpectedSmartServerResponse(response_tuple)
946
1538
body = response_handler.read_body_bytes()
948
for line in body.split('\n'):
1540
for line in body.split(b'\n'):
951
key, val_text = line.split(':')
1543
key, val_text = line.split(b':')
1544
key = key.decode('ascii')
952
1545
if key in ('revisions', 'size', 'committers'):
953
1546
result[key] = int(val_text)
954
1547
elif key in ('firstrev', 'latestrev'):
955
values = val_text.split(' ')[1:]
956
result[key] = (float(values[0]), long(values[1]))
1548
values = val_text.split(b' ')[1:]
1549
result[key] = (float(values[0]), int(values[1]))
1195
1828
raise errors.UnexpectedSmartServerResponse(response)
1197
1830
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)
1831
"""Create a descendent repository for new development.
1833
Unlike clone, this does not copy the settings of the repository.
1835
with self.lock_read():
1836
dest_repo = self._create_sprouting_repo(to_bzrdir, shared=False)
1837
dest_repo.fetch(self, revision_id=revision_id)
1840
def _create_sprouting_repo(self, a_controldir, shared):
1841
if not isinstance(a_controldir._format, self.controldir._format.__class__):
1842
# use target default format.
1843
dest_repo = a_controldir.create_repository()
1845
# Most control formats need the repository to be specifically
1846
# created, but on some old all-in-one formats it's not needed
1848
dest_repo = self._format.initialize(a_controldir, shared=shared)
1849
except errors.UninitializableFormat:
1850
dest_repo = a_controldir.open_repository()
1203
1851
return dest_repo
1205
1853
### These methods are just thin shims to the VFS object for now.
1207
1855
def revision_tree(self, revision_id):
1209
return self._real_repository.revision_tree(revision_id)
1856
with self.lock_read():
1857
revision_id = _mod_revision.ensure_null(revision_id)
1858
if revision_id == _mod_revision.NULL_REVISION:
1859
return InventoryRevisionTree(self,
1860
Inventory(root_id=None), _mod_revision.NULL_REVISION)
1862
return list(self.revision_trees([revision_id]))[0]
1211
1864
def get_serializer_format(self):
1213
return self._real_repository.get_serializer_format()
1865
path = self.controldir._path_for_remote_call(self._client)
1867
response = self._call(b'VersionedFileRepository.get_serializer_format',
1869
except errors.UnknownSmartMethod:
1871
return self._real_repository.get_serializer_format()
1872
if response[0] != b'ok':
1873
raise errors.UnexpectedSmartServerResponse(response)
1215
1876
def get_commit_builder(self, branch, parents, config, timestamp=None,
1216
1877
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)
1878
revision_id=None, lossy=False):
1879
"""Obtain a CommitBuilder for this repository.
1881
:param branch: Branch to commit to.
1882
:param parents: Revision ids of the parents of the new revision.
1883
:param config: Configuration to use.
1884
:param timestamp: Optional timestamp recorded for commit.
1885
:param timezone: Optional timezone for timestamp.
1886
:param committer: Optional committer to set for commit.
1887
:param revprops: Optional dictionary of revision properties.
1888
:param revision_id: Optional revision id.
1889
:param lossy: Whether to discard data that can not be natively
1890
represented, when pushing to a foreign VCS
1892
if self._fallback_repositories and not self._format.supports_chks:
1893
raise errors.BzrError("Cannot commit directly to a stacked branch"
1894
" in pre-2a formats. See "
1895
"https://bugs.launchpad.net/bzr/+bug/375013 for details.")
1896
commit_builder_kls = vf_repository.VersionedFileCommitBuilder
1897
result = commit_builder_kls(self, parents, config,
1898
timestamp, timezone, committer, revprops, revision_id,
1900
self.start_write_group()
1227
1903
def add_fallback_repository(self, repository):
1228
1904
"""Add a repository to use for looking up data not held locally.
1272
1949
delta, new_revision_id, parents, basis_inv=basis_inv,
1273
1950
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)
1952
def add_revision(self, revision_id, rev, inv=None):
1953
_mod_revision.check_not_reserved_id(revision_id)
1954
key = (revision_id,)
1955
# check inventory present
1956
if not self.inventories.get_parent_map([key]):
1958
raise errors.WeaveRevisionNotPresent(revision_id,
1961
# yes, this is not suitable for adding with ghosts.
1962
rev.inventory_sha1 = self.add_inventory(revision_id, inv,
1965
rev.inventory_sha1 = self.inventories.get_sha1s([key])[key]
1966
self._add_revision(rev)
1968
def _add_revision(self, rev):
1969
if self._real_repository is not None:
1970
return self._real_repository._add_revision(rev)
1971
text = self._serializer.write_revision_to_string(rev)
1972
key = (rev.revision_id,)
1973
parents = tuple((parent,) for parent in rev.parent_ids)
1974
self._write_group_tokens, missing_keys = self._get_sink().insert_stream(
1975
[('revisions', [FulltextContentFactory(key, parents, None, text)])],
1976
self._format, self._write_group_tokens)
1281
1978
def get_inventory(self, revision_id):
1979
with self.lock_read():
1980
return list(self.iter_inventories([revision_id]))[0]
1982
def _iter_inventories_rpc(self, revision_ids, ordering):
1983
if ordering is None:
1984
ordering = 'unordered'
1985
path = self.controldir._path_for_remote_call(self._client)
1986
body = b"\n".join(revision_ids)
1987
response_tuple, response_handler = (
1988
self._call_with_body_bytes_expecting_body(
1989
b"VersionedFileRepository.get_inventories",
1990
(path, ordering.encode('ascii')), body))
1991
if response_tuple[0] != b"ok":
1992
raise errors.UnexpectedSmartServerResponse(response_tuple)
1993
deserializer = inventory_delta.InventoryDeltaDeserializer()
1994
byte_stream = response_handler.read_streamed_body()
1995
decoded = smart_repo._byte_stream_to_stream(byte_stream)
1997
# no results whatsoever
1999
src_format, stream = decoded
2000
if src_format.network_name() != self._format.network_name():
2001
raise AssertionError(
2002
"Mismatched RemoteRepository and stream src %r, %r" % (
2003
src_format.network_name(), self._format.network_name()))
2004
# ignore the src format, it's not really relevant
2005
prev_inv = Inventory(root_id=None,
2006
revision_id=_mod_revision.NULL_REVISION)
2007
# there should be just one substream, with inventory deltas
2009
substream_kind, substream = next(stream)
2010
except StopIteration:
2012
if substream_kind != "inventory-deltas":
2013
raise AssertionError(
2014
"Unexpected stream %r received" % substream_kind)
2015
for record in substream:
2016
(parent_id, new_id, versioned_root, tree_references, invdelta) = (
2017
deserializer.parse_text_bytes(record.get_bytes_as("fulltext")))
2018
if parent_id != prev_inv.revision_id:
2019
raise AssertionError("invalid base %r != %r" % (parent_id,
2020
prev_inv.revision_id))
2021
inv = prev_inv.create_by_apply_delta(invdelta, new_id)
2022
yield inv, inv.revision_id
2025
def _iter_inventories_vfs(self, revision_ids, ordering=None):
1282
2026
self._ensure_real()
1283
return self._real_repository.get_inventory(revision_id)
2027
return self._real_repository._iter_inventories(revision_ids, ordering)
1285
2029
def iter_inventories(self, revision_ids, ordering=None):
1287
return self._real_repository.iter_inventories(revision_ids, ordering)
2030
"""Get many inventories by revision_ids.
2032
This will buffer some or all of the texts used in constructing the
2033
inventories in memory, but will only parse a single inventory at a
2036
:param revision_ids: The expected revision ids of the inventories.
2037
:param ordering: optional ordering, e.g. 'topological'. If not
2038
specified, the order of revision_ids will be preserved (by
2039
buffering if necessary).
2040
:return: An iterator of inventories.
2042
if ((None in revision_ids)
2043
or (_mod_revision.NULL_REVISION in revision_ids)):
2044
raise ValueError('cannot get null revision inventory')
2045
for inv, revid in self._iter_inventories(revision_ids, ordering):
2047
raise errors.NoSuchRevision(self, revid)
2050
def _iter_inventories(self, revision_ids, ordering=None):
2051
if len(revision_ids) == 0:
2053
missing = set(revision_ids)
2054
if ordering is None:
2055
order_as_requested = True
2057
order = list(revision_ids)
2059
next_revid = order.pop()
2061
order_as_requested = False
2062
if ordering != 'unordered' and self._fallback_repositories:
2063
raise ValueError('unsupported ordering %r' % ordering)
2064
iter_inv_fns = [self._iter_inventories_rpc] + [
2065
fallback._iter_inventories for fallback in
2066
self._fallback_repositories]
2068
for iter_inv in iter_inv_fns:
2069
request = [revid for revid in revision_ids if revid in missing]
2070
for inv, revid in iter_inv(request, ordering):
2073
missing.remove(inv.revision_id)
2074
if ordering != 'unordered':
2078
if order_as_requested:
2079
# Yield as many results as we can while preserving order.
2080
while next_revid in invs:
2081
inv = invs.pop(next_revid)
2082
yield inv, inv.revision_id
2084
next_revid = order.pop()
2086
# We still want to fully consume the stream, just
2087
# in case it is not actually finished at this point
2090
except errors.UnknownSmartMethod:
2091
for inv, revid in self._iter_inventories_vfs(revision_ids, ordering):
2095
if order_as_requested:
2096
if next_revid is not None:
2097
yield None, next_revid
2100
yield invs.get(revid), revid
2103
yield None, missing.pop()
1290
2105
def get_revision(self, revision_id):
1292
return self._real_repository.get_revision(revision_id)
2106
with self.lock_read():
2107
return self.get_revisions([revision_id])[0]
1294
2109
def get_transaction(self):
1295
2110
self._ensure_real()
1296
2111
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)
2113
def clone(self, a_controldir, revision_id=None):
2114
with self.lock_read():
2115
dest_repo = self._create_sprouting_repo(
2116
a_controldir, shared=self.is_shared())
2117
self.copy_content_into(dest_repo, revision_id)
1303
2120
def make_working_trees(self):
1304
2121
"""See Repository.make_working_trees"""
1306
return self._real_repository.make_working_trees()
2122
path = self.controldir._path_for_remote_call(self._client)
2124
response = self._call(b'Repository.make_working_trees', path)
2125
except errors.UnknownSmartMethod:
2127
return self._real_repository.make_working_trees()
2128
if response[0] not in (b'yes', b'no'):
2129
raise SmartProtocolError('unexpected response code %s' % (response,))
2130
return response[0] == b'yes'
1308
2132
def refresh_data(self):
1309
"""Re-read any data needed to to synchronise with disk.
2133
"""Re-read any data needed to synchronise with disk.
1311
2135
This method is intended to be called after another repository instance
1312
2136
(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.
2137
repository. On all repositories this will work outside of write groups.
2138
Some repository formats (pack and newer for breezy native formats)
2139
support refresh_data inside write groups. If called inside a write
2140
group on a repository that does not support refreshing in a write group
2141
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
2143
if self._real_repository is not None:
1320
2144
self._real_repository.refresh_data()
2145
# Refresh the parents cache for this object
2146
self._unstacked_provider.disable_cache()
2147
self._unstacked_provider.enable_cache()
1322
2149
def revision_ids_to_search_result(self, result_set):
1323
2150
"""Convert a set of revision ids to a graph SearchResult."""
1324
2151
result_parents = set()
1325
for parents in self.get_graph().get_parent_map(
1326
result_set).itervalues():
2152
for parents in viewvalues(self.get_graph().get_parent_map(result_set)):
1327
2153
result_parents.update(parents)
1328
2154
included_keys = result_set.intersection(result_parents)
1329
2155
start_keys = result_set.difference(included_keys)
1330
2156
exclude_keys = result_parents.difference(result_set)
1331
result = graph.SearchResult(start_keys, exclude_keys,
2157
result = vf_search.SearchResult(start_keys, exclude_keys,
1332
2158
len(result_set), result_set)
1336
def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
2161
def search_missing_revision_ids(self, other,
2162
find_ghosts=True, revision_ids=None, if_present_ids=None,
1337
2164
"""Return the revision ids that other has that this does not.
1339
2166
These are returned in topological order.
1341
2168
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)
2170
with self.lock_read():
2171
inter_repo = _mod_repository.InterRepository.get(other, self)
2172
return inter_repo.search_missing_revision_ids(
2173
find_ghosts=find_ghosts, revision_ids=revision_ids,
2174
if_present_ids=if_present_ids, limit=limit)
1346
def fetch(self, source, revision_id=None, pb=None, find_ghosts=False,
2176
def fetch(self, source, revision_id=None, find_ghosts=False,
1347
2177
fetch_spec=None):
1348
2178
# No base implementation to use as RemoteRepository is not a subclass
1349
2179
# of Repository; so this is a copy of Repository.fetch().
1388
2217
return self._real_repository._get_versioned_file_checker(
1389
2218
revisions, revision_versions_cache)
2220
def _iter_files_bytes_rpc(self, desired_files, absent):
2221
path = self.controldir._path_for_remote_call(self._client)
2224
for (file_id, revid, identifier) in desired_files:
2225
lines.append(b''.join([
2226
osutils.safe_file_id(file_id),
2228
osutils.safe_revision_id(revid)]))
2229
identifiers.append(identifier)
2230
(response_tuple, response_handler) = (
2231
self._call_with_body_bytes_expecting_body(
2232
b"Repository.iter_files_bytes", (path, ), b"\n".join(lines)))
2233
if response_tuple != (b'ok', ):
2234
response_handler.cancel_read_body()
2235
raise errors.UnexpectedSmartServerResponse(response_tuple)
2236
byte_stream = response_handler.read_streamed_body()
2237
def decompress_stream(start, byte_stream, unused):
2238
decompressor = zlib.decompressobj()
2239
yield decompressor.decompress(start)
2240
while decompressor.unused_data == b"":
2242
data = next(byte_stream)
2243
except StopIteration:
2245
yield decompressor.decompress(data)
2246
yield decompressor.flush()
2247
unused.append(decompressor.unused_data)
2250
while not b"\n" in unused:
2252
unused += next(byte_stream)
2253
except StopIteration:
2255
header, rest = unused.split(b"\n", 1)
2256
args = header.split(b"\0")
2257
if args[0] == b"absent":
2258
absent[identifiers[int(args[3])]] = (args[1], args[2])
2261
elif args[0] == b"ok":
2264
raise errors.UnexpectedSmartServerResponse(args)
2266
yield (identifiers[idx],
2267
decompress_stream(rest, byte_stream, unused_chunks))
2268
unused = b"".join(unused_chunks)
1391
2270
def iter_files_bytes(self, desired_files):
1392
2271
"""See Repository.iter_file_bytes.
1395
return self._real_repository.iter_files_bytes(desired_files)
2275
for (identifier, bytes_iterator) in self._iter_files_bytes_rpc(
2276
desired_files, absent):
2277
yield identifier, bytes_iterator
2278
for fallback in self._fallback_repositories:
2281
desired_files = [(key[0], key[1], identifier)
2282
for identifier, key in viewitems(absent)]
2283
for (identifier, bytes_iterator) in fallback.iter_files_bytes(desired_files):
2284
del absent[identifier]
2285
yield identifier, bytes_iterator
2287
# There may be more missing items, but raise an exception
2289
missing_identifier = next(iter(absent))
2290
missing_key = absent[missing_identifier]
2291
raise errors.RevisionNotPresent(revision_id=missing_key[1],
2292
file_id=missing_key[0])
2293
except errors.UnknownSmartMethod:
2295
for (identifier, bytes_iterator) in (
2296
self._real_repository.iter_files_bytes(desired_files)):
2297
yield identifier, bytes_iterator
2299
def get_cached_parent_map(self, revision_ids):
2300
"""See breezy.CachingParentsProvider.get_cached_parent_map"""
2301
return self._unstacked_provider.get_cached_parent_map(revision_ids)
1397
2303
def get_parent_map(self, revision_ids):
1398
"""See bzrlib.Graph.get_parent_map()."""
2304
"""See breezy.Graph.get_parent_map()."""
1399
2305
return self._make_parents_provider().get_parent_map(revision_ids)
1401
2307
def _get_parent_map_rpc(self, keys):
1529
2424
revision_graph[d[0]] = (NULL_REVISION,)
1530
2425
return revision_graph
1533
2427
def get_signature_text(self, revision_id):
1535
return self._real_repository.get_signature_text(revision_id)
2428
with self.lock_read():
2429
path = self.controldir._path_for_remote_call(self._client)
2431
response_tuple, response_handler = self._call_expecting_body(
2432
b'Repository.get_revision_signature_text', path, revision_id)
2433
except errors.UnknownSmartMethod:
2435
return self._real_repository.get_signature_text(revision_id)
2436
except errors.NoSuchRevision as err:
2437
for fallback in self._fallback_repositories:
2439
return fallback.get_signature_text(revision_id)
2440
except errors.NoSuchRevision:
2444
if response_tuple[0] != b'ok':
2445
raise errors.UnexpectedSmartServerResponse(response_tuple)
2446
return response_handler.read_body_bytes()
1538
2448
def _get_inventory_xml(self, revision_id):
1540
return self._real_repository._get_inventory_xml(revision_id)
2449
with self.lock_read():
2450
# This call is used by older working tree formats,
2451
# which stored a serialized basis inventory.
2453
return self._real_repository._get_inventory_xml(revision_id)
1542
2455
def reconcile(self, other=None, thorough=False):
1544
return self._real_repository.reconcile(other=other, thorough=thorough)
2456
from ..reconcile import RepoReconciler
2457
with self.lock_write():
2458
path = self.controldir._path_for_remote_call(self._client)
2460
response, handler = self._call_expecting_body(
2461
b'Repository.reconcile', path, self._lock_token)
2462
except (errors.UnknownSmartMethod, errors.TokenLockingNotSupported):
2464
return self._real_repository.reconcile(other=other, thorough=thorough)
2465
if response != (b'ok', ):
2466
raise errors.UnexpectedSmartServerResponse(response)
2467
body = handler.read_body_bytes()
2468
result = RepoReconciler(self)
2469
for line in body.split(b'\n'):
2472
key, val_text = line.split(b':')
2473
if key == b"garbage_inventories":
2474
result.garbage_inventories = int(val_text)
2475
elif key == b"inconsistent_parents":
2476
result.inconsistent_parents = int(val_text)
2478
mutter("unknown reconcile key %r" % key)
1546
2481
def all_revision_ids(self):
1548
return self._real_repository.all_revision_ids()
2482
path = self.controldir._path_for_remote_call(self._client)
2484
response_tuple, response_handler = self._call_expecting_body(
2485
b"Repository.all_revision_ids", path)
2486
except errors.UnknownSmartMethod:
2488
return self._real_repository.all_revision_ids()
2489
if response_tuple != (b"ok", ):
2490
raise errors.UnexpectedSmartServerResponse(response_tuple)
2491
revids = set(response_handler.read_body_bytes().splitlines())
2492
for fallback in self._fallback_repositories:
2493
revids.update(set(fallback.all_revision_ids()))
2496
def _filtered_revision_trees(self, revision_ids, file_ids):
2497
"""Return Tree for a revision on this branch with only some files.
2499
:param revision_ids: a sequence of revision-ids;
2500
a revision-id may not be None or b'null:'
2501
:param file_ids: if not None, the result is filtered
2502
so that only those file-ids, their parents and their
2503
children are included.
2505
inventories = self.iter_inventories(revision_ids)
2506
for inv in inventories:
2507
# Should we introduce a FilteredRevisionTree class rather
2508
# than pre-filter the inventory here?
2509
filtered_inv = inv.filter(file_ids)
2510
yield InventoryRevisionTree(self, filtered_inv, filtered_inv.revision_id)
1551
2512
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)
2513
with self.lock_read():
2514
medium = self._client._medium
2515
if medium._is_remote_before((1, 2)):
2517
for delta in self._real_repository.get_deltas_for_revisions(
2518
revisions, specific_fileids):
2521
# Get the revision-ids of interest
2522
required_trees = set()
2523
for revision in revisions:
2524
required_trees.add(revision.revision_id)
2525
required_trees.update(revision.parent_ids[:1])
2527
# Get the matching filtered trees. Note that it's more
2528
# efficient to pass filtered trees to changes_from() rather
2529
# than doing the filtering afterwards. changes_from() could
2530
# arguably do the filtering itself but it's path-based, not
2531
# file-id based, so filtering before or afterwards is
2533
if specific_fileids is None:
2534
trees = dict((t.get_revision_id(), t) for
2535
t in self.revision_trees(required_trees))
2537
trees = dict((t.get_revision_id(), t) for
2538
t in self._filtered_revision_trees(required_trees,
2541
# Calculate the deltas
2542
for revision in revisions:
2543
if not revision.parent_ids:
2544
old_tree = self.revision_tree(_mod_revision.NULL_REVISION)
2546
old_tree = trees[revision.parent_ids[0]]
2547
yield trees[revision.revision_id].changes_from(old_tree)
1557
2549
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)
2550
with self.lock_read():
2551
r = self.get_revision(revision_id)
2552
return list(self.get_deltas_for_revisions([r],
2553
specific_fileids=specific_fileids))[0]
1563
2555
def revision_trees(self, revision_ids):
1565
return self._real_repository.revision_trees(revision_ids)
2556
with self.lock_read():
2557
inventories = self.iter_inventories(revision_ids)
2558
for inv in inventories:
2559
yield RemoteInventoryTree(self, inv, inv.revision_id)
1568
2561
def get_revision_reconcile(self, revision_id):
1570
return self._real_repository.get_revision_reconcile(revision_id)
2562
with self.lock_read():
2564
return self._real_repository.get_revision_reconcile(revision_id)
1573
2566
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)
2567
with self.lock_read():
2569
return self._real_repository.check(revision_ids=revision_ids,
2570
callback_refs=callback_refs, check_repo=check_repo)
1578
2572
def copy_content_into(self, destination, revision_id=None):
1580
return self._real_repository.copy_content_into(
1581
destination, revision_id=revision_id)
2573
"""Make a complete copy of the content in self into destination.
2575
This is a destructive operation! Do not use it on existing
2578
interrepo = _mod_repository.InterRepository.get(self, destination)
2579
return interrepo.copy_content(revision_id)
1583
2581
def _copy_repository_tarball(self, to_bzrdir, revision_id=None):
1584
2582
# get a tarball of the remote repository, and copy from that into the
1586
from bzrlib import osutils
1588
2585
# TODO: Maybe a progress bar while streaming the tarball?
1589
note("Copying repository content as tarball...")
2586
note(gettext("Copying repository content as tarball..."))
1590
2587
tar_file = self._get_tarball('bz2')
1591
2588
if tar_file is None:
1680
2687
self._ensure_real()
1681
2688
return self._real_repository.texts
1684
def get_revisions(self, revision_ids):
1686
return self._real_repository.get_revisions(revision_ids)
2690
def _iter_revisions_rpc(self, revision_ids):
2691
body = b"\n".join(revision_ids)
2692
path = self.controldir._path_for_remote_call(self._client)
2693
response_tuple, response_handler = (
2694
self._call_with_body_bytes_expecting_body(
2695
b"Repository.iter_revisions", (path, ), body))
2696
if response_tuple[0] != b"ok":
2697
raise errors.UnexpectedSmartServerResponse(response_tuple)
2698
serializer_format = response_tuple[1].decode('ascii')
2699
serializer = serializer_format_registry.get(serializer_format)
2700
byte_stream = response_handler.read_streamed_body()
2701
decompressor = zlib.decompressobj()
2703
for bytes in byte_stream:
2704
chunks.append(decompressor.decompress(bytes))
2705
if decompressor.unused_data != b"":
2706
chunks.append(decompressor.flush())
2707
yield serializer.read_revision_from_string(b"".join(chunks))
2708
unused = decompressor.unused_data
2709
decompressor = zlib.decompressobj()
2710
chunks = [decompressor.decompress(unused)]
2711
chunks.append(decompressor.flush())
2712
text = b"".join(chunks)
2714
yield serializer.read_revision_from_string(b"".join(chunks))
2716
def iter_revisions(self, revision_ids):
2717
for rev_id in revision_ids:
2718
if not rev_id or not isinstance(rev_id, bytes):
2719
raise errors.InvalidRevisionId(
2720
revision_id=rev_id, branch=self)
2721
with self.lock_read():
2723
missing = set(revision_ids)
2724
for rev in self._iter_revisions_rpc(revision_ids):
2725
missing.remove(rev.revision_id)
2726
yield (rev.revision_id, rev)
2727
for fallback in self._fallback_repositories:
2730
for (revid, rev) in fallback.iter_revisions(missing):
2733
missing.remove(revid)
2734
for revid in missing:
2736
except errors.UnknownSmartMethod:
2738
for entry in self._real_repository.iter_revisions(revision_ids):
1688
2741
def supports_rich_root(self):
1689
2742
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
2745
def _serializer(self):
1697
2746
return self._format._serializer
1699
2748
def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
1701
return self._real_repository.store_revision_signature(
1702
gpg_strategy, plaintext, revision_id)
2749
with self.lock_write():
2750
signature = gpg_strategy.sign(plaintext, gpg.MODE_CLEAR)
2751
self.add_signature_text(revision_id, signature)
1704
2753
def add_signature_text(self, revision_id, signature):
1706
return self._real_repository.add_signature_text(revision_id, signature)
2754
if self._real_repository:
2755
# If there is a real repository the write group will
2756
# be in the real repository as well, so use that:
2758
return self._real_repository.add_signature_text(
2759
revision_id, signature)
2760
path = self.controldir._path_for_remote_call(self._client)
2761
response, handler = self._call_with_body_bytes_expecting_body(
2762
b'Repository.add_signature_text', (path, self._lock_token,
2764
tuple([token.encode('utf-8') for token in self._write_group_tokens]),
2766
handler.cancel_read_body()
2768
if response[0] != b'ok':
2769
raise errors.UnexpectedSmartServerResponse(response)
2770
self._write_group_tokens = [token.decode('utf-8') for token in response[1:]]
1708
2772
def has_signature_for_revision_id(self, revision_id):
1710
return self._real_repository.has_signature_for_revision_id(revision_id)
2773
path = self.controldir._path_for_remote_call(self._client)
2775
response = self._call(b'Repository.has_signature_for_revision_id',
2777
except errors.UnknownSmartMethod:
2779
return self._real_repository.has_signature_for_revision_id(
2781
if response[0] not in (b'yes', b'no'):
2782
raise SmartProtocolError('unexpected response code %s' % (response,))
2783
if response[0] == b'yes':
2785
for fallback in self._fallback_repositories:
2786
if fallback.has_signature_for_revision_id(revision_id):
2790
def verify_revision_signature(self, revision_id, gpg_strategy):
2791
with self.lock_read():
2792
if not self.has_signature_for_revision_id(revision_id):
2793
return gpg.SIGNATURE_NOT_SIGNED, None
2794
signature = self.get_signature_text(revision_id)
2796
testament = _mod_testament.Testament.from_revision(self, revision_id)
2798
(status, key, signed_plaintext) = gpg_strategy.verify(signature)
2799
if testament.as_short_text() != signed_plaintext:
2800
return gpg.SIGNATURE_NOT_VALID, None
2801
return (status, key)
1712
2803
def item_keys_introduced_by(self, revision_ids, _files_pb=None):
1713
2804
self._ensure_real()
1714
2805
return self._real_repository.item_keys_introduced_by(revision_ids,
1715
2806
_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
2808
def _find_inconsistent_revision_parents(self, revisions_iterator=None):
1723
2809
self._ensure_real()
1724
2810
return self._real_repository._find_inconsistent_revision_parents(
1742
2827
:param recipe: A search recipe (start, stop, count).
1743
2828
: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))
2830
start_keys = b' '.join(recipe[1])
2831
stop_keys = b' '.join(recipe[2])
2832
count = str(recipe[3]).encode('ascii')
2833
return b'\n'.join((start_keys, stop_keys, count))
1750
2835
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)
2836
parts = search_result.get_network_struct()
2837
return b'\n'.join(parts)
1759
2839
def autopack(self):
1760
path = self.bzrdir._path_for_remote_call(self._client)
2840
path = self.controldir._path_for_remote_call(self._client)
1762
response = self._call('PackRepository.autopack', path)
2842
response = self._call(b'PackRepository.autopack', path)
1763
2843
except errors.UnknownSmartMethod:
1764
2844
self._ensure_real()
1765
2845
self._real_repository._pack_collection.autopack()
1767
2847
self.refresh_data()
1768
if response[0] != 'ok':
1769
raise errors.UnexpectedSmartServerResponse(response)
1772
class RemoteStreamSink(repository.StreamSink):
2848
if response[0] != b'ok':
2849
raise errors.UnexpectedSmartServerResponse(response)
2851
def _revision_archive(self, revision_id, format, name, root, subdir,
2853
path = self.controldir._path_for_remote_call(self._client)
2854
format = format or ''
2856
subdir = subdir or ''
2857
force_mtime = int(force_mtime) if force_mtime is not None else None
2859
response, protocol = self._call_expecting_body(
2860
b'Repository.revision_archive', path,
2862
format.encode('ascii'),
2863
os.path.basename(name).encode('utf-8'),
2864
root.encode('utf-8'),
2865
subdir.encode('utf-8'),
2867
except errors.UnknownSmartMethod:
2869
if response[0] == b'ok':
2870
return iter([protocol.read_body_bytes()])
2871
raise errors.UnexpectedSmartServerResponse(response)
2873
def _annotate_file_revision(self, revid, tree_path, file_id, default_revision):
2874
path = self.controldir._path_for_remote_call(self._client)
2875
tree_path = tree_path.encode('utf-8')
2876
file_id = file_id or b''
2877
default_revision = default_revision or b''
2879
response, handler = self._call_expecting_body(
2880
b'Repository.annotate_file_revision', path,
2881
revid, tree_path, file_id, default_revision)
2882
except errors.UnknownSmartMethod:
2884
if response[0] != b'ok':
2885
raise errors.UnexpectedSmartServerResponse(response)
2886
return map(tuple, bencode.bdecode(handler.read_body_bytes()))
2889
class RemoteStreamSink(vf_repository.StreamSink):
1774
2891
def _insert_real(self, stream, src_format, resume_tokens):
1775
2892
self.target_repo._ensure_real()
2059
3231
def network_name(self):
2060
3232
return self._network_name
2062
def open(self, a_bzrdir, name=None, ignore_fallbacks=False):
2063
return a_bzrdir.open_branch(name=name,
3234
def open(self, a_controldir, name=None, ignore_fallbacks=False):
3235
return a_controldir.open_branch(name=name,
2064
3236
ignore_fallbacks=ignore_fallbacks)
2066
def _vfs_initialize(self, a_bzrdir, name):
3238
def _vfs_initialize(self, a_controldir, name, append_revisions_only,
2067
3240
# Initialisation when using a local bzrdir object, or a non-vfs init
2068
3241
# method is not available on the server.
2069
3242
# 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,
3244
if isinstance(a_controldir, RemoteBzrDir):
3245
a_controldir._ensure_real()
3246
result = self._custom_format.initialize(a_controldir._real_bzrdir,
3247
name=name, append_revisions_only=append_revisions_only,
3248
repository=repository)
2076
3250
# 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
3251
result = self._custom_format.initialize(a_controldir, name=name,
3252
append_revisions_only=append_revisions_only,
3253
repository=repository)
3254
if (isinstance(a_controldir, RemoteBzrDir) and
2079
3255
not isinstance(result, RemoteBranch)):
2080
result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result,
3256
result = RemoteBranch(a_controldir, a_controldir.find_repository(), result,
2084
def initialize(self, a_bzrdir, name=None):
3260
def initialize(self, a_controldir, name=None, repository=None,
3261
append_revisions_only=None):
3263
name = a_controldir._get_selected_branch()
2085
3264
# 1) get the network name to use.
2086
3265
if self._custom_format:
2087
3266
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')()
3268
# Select the current breezy default and ask for that.
3269
reference_bzrdir_format = controldir.format_registry.get('default')()
2091
3270
reference_format = reference_bzrdir_format.get_branch_format()
2092
3271
self._custom_format = reference_format
2093
3272
network_name = reference_format.network_name()
2094
3273
# 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
3274
if not isinstance(a_controldir, RemoteBzrDir):
3275
return self._vfs_initialize(a_controldir, name=name,
3276
append_revisions_only=append_revisions_only,
3277
repository=repository)
3278
medium = a_controldir._client._medium
2098
3279
if medium._is_remote_before((1, 13)):
2099
return self._vfs_initialize(a_bzrdir, name=name)
3280
return self._vfs_initialize(a_controldir, name=name,
3281
append_revisions_only=append_revisions_only,
3282
repository=repository)
2100
3283
# Creating on a remote bzr dir.
2101
3284
# 2) try direct creation via RPC
2102
path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
2103
if name is not None:
3285
path = a_controldir._path_for_remote_call(a_controldir._client)
2104
3287
# XXX JRV20100304: Support creating colocated branches
2105
3288
raise errors.NoColocatedBranchSupport(self)
2106
verb = 'BzrDir.create_branch'
3289
verb = b'BzrDir.create_branch'
2108
response = a_bzrdir._call(verb, path, network_name)
3291
response = a_controldir._call(verb, path, network_name)
2109
3292
except errors.UnknownSmartMethod:
2110
3293
# Fallback - use vfs methods
2111
3294
medium._remember_remote_is_before((1, 13))
2112
return self._vfs_initialize(a_bzrdir, name=name)
2113
if response[0] != 'ok':
3295
return self._vfs_initialize(a_controldir, name=name,
3296
append_revisions_only=append_revisions_only,
3297
repository=repository)
3298
if response[0] != b'ok':
2114
3299
raise errors.UnexpectedSmartServerResponse(response)
2115
3300
# Turn the response into a RemoteRepository object.
2116
3301
format = RemoteBranchFormat(network_name=response[1])
2117
3302
repo_format = response_tuple_to_repo_format(response[3:])
2118
if response[2] == '':
2119
repo_bzrdir = a_bzrdir
3303
repo_path = response[2].decode('utf-8')
3304
if repository is not None:
3305
remote_repo_url = urlutils.join(a_controldir.user_url, repo_path)
3306
url_diff = urlutils.relative_url(repository.user_url,
3309
raise AssertionError(
3310
'repository.user_url %r does not match URL from server '
3311
'response (%r + %r)'
3312
% (repository.user_url, a_controldir.user_url, repo_path))
3313
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,
3316
repo_bzrdir = a_controldir
3318
repo_bzrdir = RemoteBzrDir(
3319
a_controldir.root_transport.clone(repo_path), a_controldir._format,
3320
a_controldir._client)
3321
remote_repo = RemoteRepository(repo_bzrdir, repo_format)
3322
remote_branch = RemoteBranch(a_controldir, remote_repo,
2126
3323
format=format, setup_stacking=False, name=name)
3324
if append_revisions_only:
3325
remote_branch.set_append_revisions_only(append_revisions_only)
2127
3326
# XXX: We know this is a new branch, so it must have revno 0, revid
2128
3327
# NULL_REVISION. Creating the branch locked would make this be unable
2129
3328
# to be wrong; here its simply very unlikely to be wrong. RBC 20090225
2148
3347
self._ensure_real()
2149
3348
return self._custom_format.supports_set_append_revisions_only()
3350
def _use_default_local_heads_to_fetch(self):
3351
# If the branch format is a metadir format *and* its heads_to_fetch
3352
# implementation is not overridden vs the base class, we can use the
3353
# base class logic rather than use the heads_to_fetch RPC. This is
3354
# usually cheaper in terms of net round trips, as the last-revision and
3355
# tags info fetched is cached and would be fetched anyway.
3357
if isinstance(self._custom_format, bzrbranch.BranchFormatMetadir):
3358
branch_class = self._custom_format._branch_class()
3359
heads_to_fetch_impl = get_unbound_function(branch_class.heads_to_fetch)
3360
if heads_to_fetch_impl is get_unbound_function(branch.Branch.heads_to_fetch):
3365
class RemoteBranchStore(_mod_config.IniFileStore):
3366
"""Branch store which attempts to use HPSS calls to retrieve branch store.
3368
Note that this is specific to bzr-based formats.
3371
def __init__(self, branch):
3372
super(RemoteBranchStore, self).__init__()
3373
self.branch = branch
3375
self._real_store = None
3377
def external_url(self):
3378
return urlutils.join(self.branch.user_url, 'branch.conf')
3380
def _load_content(self):
3381
path = self.branch._remote_path()
3383
response, handler = self.branch._call_expecting_body(
3384
b'Branch.get_config_file', path)
3385
except errors.UnknownSmartMethod:
3387
return self._real_store._load_content()
3388
if len(response) and response[0] != b'ok':
3389
raise errors.UnexpectedSmartServerResponse(response)
3390
return handler.read_body_bytes()
3392
def _save_content(self, content):
3393
path = self.branch._remote_path()
3395
response, handler = self.branch._call_with_body_bytes_expecting_body(
3396
b'Branch.put_config_file', (path,
3397
self.branch._lock_token, self.branch._repo_lock_token),
3399
except errors.UnknownSmartMethod:
3401
return self._real_store._save_content(content)
3402
handler.cancel_read_body()
3403
if response != (b'ok', ):
3404
raise errors.UnexpectedSmartServerResponse(response)
3406
def _ensure_real(self):
3407
self.branch._ensure_real()
3408
if self._real_store is None:
3409
self._real_store = _mod_config.BranchStore(self.branch)
2152
3412
class RemoteBranch(branch.Branch, _RpcHelper, lock._RelockDebugMixin):
2153
3413
"""Branch stored on a server accessed by HPSS RPC.
2644
3956
self._ensure_real()
2645
3957
return self._real_branch._set_parent_location(url)
2648
3959
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)
3961
with self.lock_write():
3962
self._clear_cached_state_of_remote_branch_only()
3964
return self._real_branch.pull(
3965
source, overwrite=overwrite, stop_revision=stop_revision,
3966
_override_hook_target=self, **kwargs)
3968
def push(self, target, overwrite=False, stop_revision=None, lossy=False):
3969
with self.lock_read():
3971
return self._real_branch.push(
3972
target, overwrite=overwrite, stop_revision=stop_revision, lossy=lossy,
3973
_override_hook_source_branch=self)
3975
def peek_lock_mode(self):
3976
return self._lock_mode
2663
3978
def is_locked(self):
2664
3979
return self._lock_count >= 1
3981
def revision_id_to_dotted_revno(self, revision_id):
3982
"""Given a revision id, return its dotted revno.
3984
:return: a tuple like (1,) or (400,1,3).
3986
with self.lock_read():
3988
response = self._call(b'Branch.revision_id_to_revno',
3989
self._remote_path(), revision_id)
3990
except errors.UnknownSmartMethod:
3992
return self._real_branch.revision_id_to_dotted_revno(revision_id)
3993
if response[0] == b'ok':
3994
return tuple([int(x) for x in response[1:]])
3996
raise errors.UnexpectedSmartServerResponse(response)
2667
3998
def revision_id_to_revno(self, revision_id):
2669
return self._real_branch.revision_id_to_revno(revision_id)
3999
"""Given a revision id on the branch mainline, return its revno.
4003
with self.lock_read():
4005
response = self._call(b'Branch.revision_id_to_revno',
4006
self._remote_path(), revision_id)
4007
except errors.UnknownSmartMethod:
4009
return self._real_branch.revision_id_to_revno(revision_id)
4010
if response[0] == b'ok':
4011
if len(response) == 2:
4012
return int(response[1])
4013
raise NoSuchRevision(self, revision_id)
4015
raise errors.UnexpectedSmartServerResponse(response)
2672
4017
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)
4018
with self.lock_write():
4019
# XXX: These should be returned by the set_last_revision_info verb
4020
old_revno, old_revid = self.last_revision_info()
4021
self._run_pre_change_branch_tip_hooks(revno, revision_id)
4022
if not revision_id or not isinstance(revision_id, bytes):
4023
raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
4025
response = self._call(b'Branch.set_last_revision_info',
4026
self._remote_path(), self._lock_token, self._repo_lock_token,
4027
str(revno).encode('ascii'), revision_id)
4028
except errors.UnknownSmartMethod:
4030
self._clear_cached_state_of_remote_branch_only()
4031
self._real_branch.set_last_revision_info(revno, revision_id)
4032
self._last_revision_info_cache = revno, revision_id
4034
if response == (b'ok',):
4035
self._clear_cached_state()
4036
self._last_revision_info_cache = revno, revision_id
4037
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
4038
# Update the _real_branch's cache too.
4039
if self._real_branch is not None:
4040
cache = self._last_revision_info_cache
4041
self._real_branch._last_revision_info_cache = cache
4043
raise errors.UnexpectedSmartServerResponse(response)
2699
4045
def generate_revision_history(self, revision_id, last_rev=None,
2700
4046
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))
4047
with self.lock_write():
4048
medium = self._client._medium
4049
if not medium._is_remote_before((1, 6)):
4050
# Use a smart method for 1.6 and above servers
4052
self._set_last_revision_descendant(revision_id, other_branch,
4053
allow_diverged=True, allow_overwrite_descendant=True)
4055
except errors.UnknownSmartMethod:
4056
medium._remember_remote_is_before((1, 6))
4057
self._clear_cached_state_of_remote_branch_only()
4058
graph = self.repository.get_graph()
4059
(last_revno, last_revid) = self.last_revision_info()
4060
known_revision_ids = [
4061
(last_revid, last_revno),
4062
(_mod_revision.NULL_REVISION, 0),
4064
if last_rev is not None:
4065
if not graph.is_ancestor(last_rev, revision_id):
4066
# our previous tip is not merged into stop_revision
4067
raise errors.DivergedBranches(self, other_branch)
4068
revno = graph.find_distance_to_null(revision_id, known_revision_ids)
4069
self.set_last_revision_info(revno, revision_id)
2714
4071
def set_push_location(self, location):
4072
self._set_config_location('push_location', location)
4074
def heads_to_fetch(self):
4075
if self._format._use_default_local_heads_to_fetch():
4076
# We recognise this format, and its heads-to-fetch implementation
4077
# is the default one (tip + tags). In this case it's cheaper to
4078
# just use the default implementation rather than a special RPC as
4079
# the tip and tags data is cached.
4080
return branch.Branch.heads_to_fetch(self)
4081
medium = self._client._medium
4082
if medium._is_remote_before((2, 4)):
4083
return self._vfs_heads_to_fetch()
4085
return self._rpc_heads_to_fetch()
4086
except errors.UnknownSmartMethod:
4087
medium._remember_remote_is_before((2, 4))
4088
return self._vfs_heads_to_fetch()
4090
def _rpc_heads_to_fetch(self):
4091
response = self._call(b'Branch.heads_to_fetch', self._remote_path())
4092
if len(response) != 2:
4093
raise errors.UnexpectedSmartServerResponse(response)
4094
must_fetch, if_present_fetch = response
4095
return set(must_fetch), set(if_present_fetch)
4097
def _vfs_heads_to_fetch(self):
2715
4098
self._ensure_real()
2716
return self._real_branch.set_push_location(location)
4099
return self._real_branch.heads_to_fetch()
2719
4102
class RemoteConfig(object):
2774
4167
medium = self._branch._client._medium
2775
4168
if medium._is_remote_before((1, 14)):
2776
4169
return self._vfs_set_option(value, name, section)
4170
if isinstance(value, dict):
4171
if medium._is_remote_before((2, 2)):
4172
return self._vfs_set_option(value, name, section)
4173
return self._set_config_option_dict(value, name, section)
4175
return self._set_config_option(value, name, section)
4177
def _set_config_option(self, value, name, section):
2778
4179
path = self._branch._remote_path()
2779
response = self._branch._client.call('Branch.set_config_option',
4180
response = self._branch._client.call(b'Branch.set_config_option',
2780
4181
path, self._branch._lock_token, self._branch._repo_lock_token,
2781
value.encode('utf8'), name, section or '')
4182
value.encode('utf8'), name.encode('utf-8'),
4183
(section or '').encode('utf-8'))
2782
4184
except errors.UnknownSmartMethod:
4185
medium = self._branch._client._medium
2783
4186
medium._remember_remote_is_before((1, 14))
2784
4187
return self._vfs_set_option(value, name, section)
2785
4188
if response != ():
2786
4189
raise errors.UnexpectedSmartServerResponse(response)
4191
def _serialize_option_dict(self, option_dict):
4193
for key, value in option_dict.items():
4194
if isinstance(key, text_type):
4195
key = key.encode('utf8')
4196
if isinstance(value, text_type):
4197
value = value.encode('utf8')
4198
utf8_dict[key] = value
4199
return bencode.bencode(utf8_dict)
4201
def _set_config_option_dict(self, value, name, section):
4203
path = self._branch._remote_path()
4204
serialised_dict = self._serialize_option_dict(value)
4205
response = self._branch._client.call(
4206
b'Branch.set_config_option_dict',
4207
path, self._branch._lock_token, self._branch._repo_lock_token,
4208
serialised_dict, name.encode('utf-8'), (section or '').encode('utf-8'))
4209
except errors.UnknownSmartMethod:
4210
medium = self._branch._client._medium
4211
medium._remember_remote_is_before((2, 2))
4212
return self._vfs_set_option(value, name, section)
4214
raise errors.UnexpectedSmartServerResponse(response)
2788
4216
def _real_object(self):
2789
4217
self._branch._ensure_real()
2790
4218
return self._branch._real_branch
2867
4289
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)
4292
return err.error_args[0].decode('utf-8')
4294
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
4296
if not isinstance(err.error_verb, bytes):
4297
raise TypeError(err.error_verb)
4299
translator = error_translators.get(err.error_verb)
4303
raise translator(err, find, get_path)
4305
translator = no_context_error_translators.get(err.error_verb)
4307
raise errors.UnknownErrorFromSmartServer(err)
4309
raise translator(err)
4312
error_translators.register(b'NoSuchRevision',
4313
lambda err, find, get_path: NoSuchRevision(
4314
find('branch'), err.error_args[0]))
4315
error_translators.register(b'nosuchrevision',
4316
lambda err, find, get_path: NoSuchRevision(
4317
find('repository'), err.error_args[0]))
4319
def _translate_nobranch_error(err, find, get_path):
4320
if len(err.error_args) >= 1:
4321
extra = err.error_args[0].decode('utf-8')
4324
return errors.NotBranchError(path=find('bzrdir').root_transport.base,
4327
error_translators.register(b'nobranch', _translate_nobranch_error)
4328
error_translators.register(b'norepository',
4329
lambda err, find, get_path: errors.NoRepositoryPresent(
4331
error_translators.register(b'UnlockableTransport',
4332
lambda err, find, get_path: errors.UnlockableTransport(
4333
find('bzrdir').root_transport))
4334
error_translators.register(b'TokenMismatch',
4335
lambda err, find, get_path: errors.TokenMismatch(
4336
find('token'), '(remote token)'))
4337
error_translators.register(b'Diverged',
4338
lambda err, find, get_path: errors.DivergedBranches(
4339
find('branch'), find('other_branch')))
4340
error_translators.register(b'NotStacked',
4341
lambda err, find, get_path: errors.NotStacked(branch=find('branch')))
4343
def _translate_PermissionDenied(err, find, get_path):
4345
if len(err.error_args) >= 2:
4346
extra = err.error_args[1].decode('utf-8')
4349
return errors.PermissionDenied(path, extra=extra)
4351
error_translators.register(b'PermissionDenied', _translate_PermissionDenied)
4352
error_translators.register(b'ReadError',
4353
lambda err, find, get_path: errors.ReadError(get_path()))
4354
error_translators.register(b'NoSuchFile',
4355
lambda err, find, get_path: errors.NoSuchFile(get_path()))
4356
error_translators.register(b'TokenLockingNotSupported',
4357
lambda err, find, get_path: errors.TokenLockingNotSupported(
4358
find('repository')))
4359
error_translators.register(b'UnsuspendableWriteGroup',
4360
lambda err, find, get_path: errors.UnsuspendableWriteGroup(
4361
repository=find('repository')))
4362
error_translators.register(b'UnresumableWriteGroup',
4363
lambda err, find, get_path: errors.UnresumableWriteGroup(
4364
repository=find('repository'), write_groups=err.error_args[0],
4365
reason=err.error_args[1]))
4366
no_context_error_translators.register(b'GhostRevisionsHaveNoRevno',
4367
lambda err: errors.GhostRevisionsHaveNoRevno(*err.error_args))
4368
no_context_error_translators.register(b'IncompatibleRepositories',
4369
lambda err: errors.IncompatibleRepositories(
4370
err.error_args[0].decode('utf-8'), err.error_args[1].decode('utf-8'), err.error_args[2].decode('utf-8')))
4371
no_context_error_translators.register(b'LockContention',
4372
lambda err: errors.LockContention('(remote lock)'))
4373
no_context_error_translators.register(b'LockFailed',
4374
lambda err: errors.LockFailed(err.error_args[0].decode('utf-8'), err.error_args[1].decode('utf-8')))
4375
no_context_error_translators.register(b'TipChangeRejected',
4376
lambda err: errors.TipChangeRejected(err.error_args[0].decode('utf8')))
4377
no_context_error_translators.register(b'UnstackableBranchFormat',
4378
lambda err: branch.UnstackableBranchFormat(*err.error_args))
4379
no_context_error_translators.register(b'UnstackableRepositoryFormat',
4380
lambda err: errors.UnstackableRepositoryFormat(*err.error_args))
4381
no_context_error_translators.register(b'FileExists',
4382
lambda err: errors.FileExists(err.error_args[0].decode('utf-8')))
4383
no_context_error_translators.register(b'DirectoryNotEmpty',
4384
lambda err: errors.DirectoryNotEmpty(err.error_args[0].decode('utf-8')))
4385
no_context_error_translators.register(b'UnknownFormat',
4386
lambda err: errors.UnknownFormatError(
4387
err.error_args[0].decode('ascii'), err.error_args[0].decode('ascii')))
4389
def _translate_short_readv_error(err):
4390
args = err.error_args
4391
return errors.ShortReadvError(
4392
args[0].decode('utf-8'),
4393
int(args[1].decode('ascii')), int(args[2].decode('ascii')),
4394
int(args[3].decode('ascii')))
4396
no_context_error_translators.register(b'ShortReadvError',
4397
_translate_short_readv_error)
4399
def _translate_unicode_error(err):
4400
encoding = err.error_args[0].decode('ascii')
4401
val = err.error_args[1].decode('utf-8')
4402
start = int(err.error_args[2].decode('ascii'))
4403
end = int(err.error_args[3].decode('ascii'))
4404
reason = err.error_args[4].decode('utf-8')
2937
4405
if val.startswith('u:'):
2938
4406
val = val[2:].decode('utf-8')
2939
4407
elif val.startswith('s:'):