73
104
return self._client.call_with_body_bytes_expecting_body(
74
105
method, args, body_bytes)
75
except errors.ErrorFromSmartServer, err:
106
except errors.ErrorFromSmartServer as err:
76
107
self._translate_error(err, **err_context)
79
110
def response_tuple_to_repo_format(response):
80
111
"""Convert a response tuple describing a repository format to a format."""
81
112
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')
113
format._rich_root_data = (response[0] == b'yes')
114
format._supports_tree_reference = (response[1] == b'yes')
115
format._supports_external_lookups = (response[2] == b'yes')
85
116
format._network_name = response[3]
89
# Note: RemoteBzrDirFormat is in bzrdir.py
91
class RemoteBzrDir(BzrDir, _RpcHelper):
120
# Note that RemoteBzrDirProber lives in breezy.bzrdir so breezy.bzr.remote
121
# does not have to be imported unless a remote format is involved.
123
class RemoteBzrDirFormat(_mod_bzrdir.BzrDirMetaFormat1):
124
"""Format representing bzrdirs accessed via a smart server"""
126
supports_workingtrees = False
128
colocated_branches = False
131
_mod_bzrdir.BzrDirMetaFormat1.__init__(self)
132
# XXX: It's a bit ugly that the network name is here, because we'd
133
# like to believe that format objects are stateless or at least
134
# immutable, However, we do at least avoid mutating the name after
135
# it's returned. See <https://bugs.launchpad.net/bzr/+bug/504102>
136
self._network_name = None
139
return "%s(_network_name=%r)" % (self.__class__.__name__,
142
def get_format_description(self):
143
if self._network_name:
145
real_format = controldir.network_format_registry.get(
150
return 'Remote: ' + real_format.get_format_description()
151
return 'bzr remote bzrdir'
153
def get_format_string(self):
154
raise NotImplementedError(self.get_format_string)
156
def network_name(self):
157
if self._network_name:
158
return self._network_name
160
raise AssertionError("No network name set.")
162
def initialize_on_transport(self, transport):
164
# hand off the request to the smart server
165
client_medium = transport.get_smart_medium()
166
except errors.NoSmartMedium:
167
# TODO: lookup the local format from a server hint.
168
local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
169
return local_dir_format.initialize_on_transport(transport)
170
client = _SmartClient(client_medium)
171
path = client.remote_path_from_transport(transport)
173
response = client.call(b'BzrDirFormat.initialize', path)
174
except errors.ErrorFromSmartServer as err:
175
_translate_error(err, path=path)
176
if response[0] != b'ok':
177
raise errors.SmartProtocolError('unexpected response code %s' % (response,))
178
format = RemoteBzrDirFormat()
179
self._supply_sub_formats_to(format)
180
return RemoteBzrDir(transport, format)
182
def parse_NoneTrueFalse(self, arg):
189
raise AssertionError("invalid arg %r" % arg)
191
def _serialize_NoneTrueFalse(self, arg):
198
def _serialize_NoneString(self, arg):
201
def initialize_on_transport_ex(self, transport, use_existing_dir=False,
202
create_prefix=False, force_new_repo=False, stacked_on=None,
203
stack_on_pwd=None, repo_format_name=None, make_working_trees=None,
206
# hand off the request to the smart server
207
client_medium = transport.get_smart_medium()
208
except errors.NoSmartMedium:
211
# Decline to open it if the server doesn't support our required
212
# version (3) so that the VFS-based transport will do it.
213
if client_medium.should_probe():
215
server_version = client_medium.protocol_version()
216
if server_version != '2':
220
except errors.SmartProtocolError:
221
# Apparently there's no usable smart server there, even though
222
# the medium supports the smart protocol.
227
client = _SmartClient(client_medium)
228
path = client.remote_path_from_transport(transport)
229
if client_medium._is_remote_before((1, 16)):
232
# TODO: lookup the local format from a server hint.
233
local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
234
self._supply_sub_formats_to(local_dir_format)
235
return local_dir_format.initialize_on_transport_ex(transport,
236
use_existing_dir=use_existing_dir, create_prefix=create_prefix,
237
force_new_repo=force_new_repo, stacked_on=stacked_on,
238
stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
239
make_working_trees=make_working_trees, shared_repo=shared_repo,
241
return self._initialize_on_transport_ex_rpc(client, path, transport,
242
use_existing_dir, create_prefix, force_new_repo, stacked_on,
243
stack_on_pwd, repo_format_name, make_working_trees, shared_repo)
245
def _initialize_on_transport_ex_rpc(self, client, path, transport,
246
use_existing_dir, create_prefix, force_new_repo, stacked_on,
247
stack_on_pwd, repo_format_name, make_working_trees, shared_repo):
249
args.append(self._serialize_NoneTrueFalse(use_existing_dir))
250
args.append(self._serialize_NoneTrueFalse(create_prefix))
251
args.append(self._serialize_NoneTrueFalse(force_new_repo))
252
args.append(self._serialize_NoneString(stacked_on))
253
# stack_on_pwd is often/usually our transport
256
stack_on_pwd = transport.relpath(stack_on_pwd).encode('utf-8')
259
except errors.PathNotChild:
261
args.append(self._serialize_NoneString(stack_on_pwd))
262
args.append(self._serialize_NoneString(repo_format_name))
263
args.append(self._serialize_NoneTrueFalse(make_working_trees))
264
args.append(self._serialize_NoneTrueFalse(shared_repo))
265
request_network_name = self._network_name or \
266
_mod_bzrdir.BzrDirFormat.get_default_format().network_name()
268
response = client.call(b'BzrDirFormat.initialize_ex_1.16',
269
request_network_name, path, *args)
270
except errors.UnknownSmartMethod:
271
client._medium._remember_remote_is_before((1, 16))
272
local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
273
self._supply_sub_formats_to(local_dir_format)
274
return local_dir_format.initialize_on_transport_ex(transport,
275
use_existing_dir=use_existing_dir, create_prefix=create_prefix,
276
force_new_repo=force_new_repo, stacked_on=stacked_on,
277
stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
278
make_working_trees=make_working_trees, shared_repo=shared_repo,
280
except errors.ErrorFromSmartServer as err:
281
_translate_error(err, path=path.decode('utf-8'))
282
repo_path = response[0]
283
bzrdir_name = response[6]
284
require_stacking = response[7]
285
require_stacking = self.parse_NoneTrueFalse(require_stacking)
286
format = RemoteBzrDirFormat()
287
format._network_name = bzrdir_name
288
self._supply_sub_formats_to(format)
289
bzrdir = RemoteBzrDir(transport, format, _client=client)
291
repo_format = response_tuple_to_repo_format(response[1:])
292
if repo_path == b'.':
294
repo_path = repo_path.decode('utf-8')
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
304
final_stack = final_stack.decode('utf-8')
305
final_stack_pwd = response[9] or None
307
final_stack_pwd = urlutils.join(
308
transport.base, final_stack_pwd.decode('utf-8'))
309
remote_repo = RemoteRepository(repo_bzr, repo_format)
310
if len(response) > 10:
311
# Updated server verb that locks remotely.
312
repo_lock_token = response[10] or None
313
remote_repo.lock_write(repo_lock_token, _skip_rpc=True)
315
remote_repo.dont_leave_lock_in_place()
317
remote_repo.lock_write()
318
policy = _mod_bzrdir.UseExistingRepository(remote_repo,
319
final_stack, final_stack_pwd, require_stacking)
320
policy.acquire_repository()
324
bzrdir._format.set_branch_format(self.get_branch_format())
326
# The repo has already been created, but we need to make sure that
327
# we'll make a stackable branch.
328
bzrdir._format.require_stacking(_skip_repo=True)
329
return remote_repo, bzrdir, require_stacking, policy
331
def _open(self, transport):
332
return RemoteBzrDir(transport, self)
334
def __eq__(self, other):
335
if not isinstance(other, RemoteBzrDirFormat):
337
return self.get_format_description() == other.get_format_description()
339
def __return_repository_format(self):
340
# Always return a RemoteRepositoryFormat object, but if a specific bzr
341
# repository format has been asked for, tell the RemoteRepositoryFormat
342
# that it should use that for init() etc.
343
result = RemoteRepositoryFormat()
344
custom_format = getattr(self, '_repository_format', None)
346
if isinstance(custom_format, RemoteRepositoryFormat):
349
# We will use the custom format to create repositories over the
350
# wire; expose its details like rich_root_data for code to
352
result._custom_format = custom_format
355
def get_branch_format(self):
356
result = _mod_bzrdir.BzrDirMetaFormat1.get_branch_format(self)
357
if not isinstance(result, RemoteBranchFormat):
358
new_result = RemoteBranchFormat()
359
new_result._custom_format = result
361
self.set_branch_format(new_result)
365
repository_format = property(__return_repository_format,
366
_mod_bzrdir.BzrDirMetaFormat1._set_repository_format) #.im_func)
369
class RemoteControlStore(_mod_config.IniFileStore):
370
"""Control store which attempts to use HPSS calls to retrieve control store.
372
Note that this is specific to bzr-based formats.
375
def __init__(self, bzrdir):
376
super(RemoteControlStore, self).__init__()
377
self.controldir = bzrdir
378
self._real_store = None
380
def lock_write(self, token=None):
382
return self._real_store.lock_write(token)
386
return self._real_store.unlock()
389
with self.lock_write():
390
# We need to be able to override the undecorated implementation
391
self.save_without_locking()
393
def save_without_locking(self):
394
super(RemoteControlStore, self).save()
396
def _ensure_real(self):
397
self.controldir._ensure_real()
398
if self._real_store is None:
399
self._real_store = _mod_config.ControlStore(self.controldir)
401
def external_url(self):
402
return urlutils.join(self.branch.user_url, 'control.conf')
404
def _load_content(self):
405
medium = self.controldir._client._medium
406
path = self.controldir._path_for_remote_call(self.controldir._client)
408
response, handler = self.controldir._call_expecting_body(
409
b'BzrDir.get_config_file', path)
410
except errors.UnknownSmartMethod:
412
return self._real_store._load_content()
413
if len(response) and response[0] != b'ok':
414
raise errors.UnexpectedSmartServerResponse(response)
415
return handler.read_body_bytes()
417
def _save_content(self, content):
418
# FIXME JRV 2011-11-22: Ideally this should use a
419
# HPSS call too, but at the moment it is not possible
420
# to write lock control directories.
422
return self._real_store._save_content(content)
425
class RemoteBzrDir(_mod_bzrdir.BzrDir, _RpcHelper):
92
426
"""Control directory on a remote server, accessed via bzr:// or similar."""
94
428
def __init__(self, transport, format, _client=None, _force_probe=False):
186
562
medium = self._client._medium
187
563
if medium._is_remote_before((1, 13)):
188
564
return self._vfs_cloning_metadir(require_stacking=require_stacking)
189
verb = 'BzrDir.cloning_metadir'
565
verb = b'BzrDir.cloning_metadir'
190
566
if require_stacking:
194
570
path = self._path_for_remote_call(self._client)
196
572
response = self._call(verb, path, stacking)
197
573
except errors.UnknownSmartMethod:
198
574
medium._remember_remote_is_before((1, 13))
199
575
return self._vfs_cloning_metadir(require_stacking=require_stacking)
200
except errors.UnknownErrorFromSmartServer, err:
201
if err.error_tuple != ('BranchReference',):
576
except errors.UnknownErrorFromSmartServer as err:
577
if err.error_tuple != (b'BranchReference',):
203
579
# We need to resolve the branch reference to determine the
204
580
# cloning_metadir. This causes unnecessary RPCs to open the
205
581
# referenced branch (and bzrdir, etc) but only when the caller
206
582
# didn't already resolve the branch reference.
207
583
referenced_branch = self.open_branch()
208
return referenced_branch.bzrdir.cloning_metadir()
584
return referenced_branch.controldir.cloning_metadir()
209
585
if len(response) != 3:
210
586
raise errors.UnexpectedSmartServerResponse(response)
211
587
control_name, repo_name, branch_info = response
212
588
if len(branch_info) != 2:
213
589
raise errors.UnexpectedSmartServerResponse(response)
214
590
branch_ref, branch_name = branch_info
215
format = bzrdir.network_format_registry.get(control_name)
592
format = controldir.network_format_registry.get(control_name)
594
raise errors.UnknownFormatError(kind='control', format=control_name)
217
format.repository_format = repository.network_format_registry.get(
219
if branch_ref == 'ref':
598
format.repository_format = _mod_repository.network_format_registry.get(
601
raise errors.UnknownFormatError(kind='repository',
603
if branch_ref == b'ref':
220
604
# XXX: we need possible_transports here to avoid reopening the
221
605
# connection to the referenced location
222
ref_bzrdir = BzrDir.open(branch_name)
606
ref_bzrdir = _mod_bzrdir.BzrDir.open(branch_name)
223
607
branch_format = ref_bzrdir.cloning_metadir().get_branch_format()
224
608
format.set_branch_format(branch_format)
225
elif branch_ref == 'branch':
609
elif branch_ref == b'branch':
227
format.set_branch_format(
228
branch.network_format_registry.get(branch_name))
612
branch_format = branch.network_format_registry.get(
615
raise errors.UnknownFormatError(kind='branch',
617
format.set_branch_format(branch_format)
230
619
raise errors.UnexpectedSmartServerResponse(response)
266
671
def destroy_branch(self, name=None):
267
672
"""See BzrDir.destroy_branch"""
269
self._real_bzrdir.destroy_branch(name=name)
674
name = self._get_selected_branch()
676
raise errors.NoColocatedBranchSupport(self)
677
path = self._path_for_remote_call(self._client)
683
response = self._call(b'BzrDir.destroy_branch', path, *args)
684
except errors.UnknownSmartMethod:
686
self._real_bzrdir.destroy_branch(name=name)
687
self._next_open_branch_result = None
270
689
self._next_open_branch_result = None
690
if response[0] != b'ok':
691
raise SmartProtocolError('unexpected response code %s' % (response,))
272
def create_workingtree(self, revision_id=None, from_branch=None):
693
def create_workingtree(self, revision_id=None, from_branch=None,
694
accelerator_tree=None, hardlink=False):
273
695
raise errors.NotLocalUrl(self.transport.base)
275
def find_branch_format(self):
697
def find_branch_format(self, name=None):
276
698
"""Find the branch 'format' for this bzrdir.
278
700
This might be a synthetic object for e.g. RemoteBranch and SVN.
280
b = self.open_branch()
702
b = self.open_branch(name=name)
283
def get_branch_reference(self):
705
def get_branches(self, possible_transports=None, ignore_fallbacks=False):
706
path = self._path_for_remote_call(self._client)
708
response, handler = self._call_expecting_body(
709
b'BzrDir.get_branches', path)
710
except errors.UnknownSmartMethod:
712
return self._real_bzrdir.get_branches()
713
if response[0] != b"success":
714
raise errors.UnexpectedSmartServerResponse(response)
715
body = bencode.bdecode(handler.read_body_bytes())
717
for name, value in viewitems(body):
718
name = name.decode('utf-8')
719
ret[name] = self._open_branch(name, value[0], value[1],
720
possible_transports=possible_transports,
721
ignore_fallbacks=ignore_fallbacks)
724
def set_branch_reference(self, target_branch, name=None):
725
"""See BzrDir.set_branch_reference()."""
727
name = self._get_selected_branch()
729
raise errors.NoColocatedBranchSupport(self)
731
return self._real_bzrdir.set_branch_reference(target_branch, name=name)
733
def get_branch_reference(self, name=None):
284
734
"""See BzrDir.get_branch_reference()."""
736
name = self._get_selected_branch()
738
raise errors.NoColocatedBranchSupport(self)
285
739
response = self._get_branch_reference()
286
740
if response[0] == 'ref':
741
return response[1].decode('utf-8')
291
745
def _get_branch_reference(self):
746
"""Get branch reference information
748
:return: Tuple with (kind, location_or_format)
749
if kind == 'ref', then location_or_format contains a location
750
otherwise, it contains a format name
292
752
path = self._path_for_remote_call(self._client)
293
753
medium = self._client._medium
294
754
candidate_calls = [
295
('BzrDir.open_branchV3', (2, 1)),
296
('BzrDir.open_branchV2', (1, 13)),
297
('BzrDir.open_branch', None),
755
(b'BzrDir.open_branchV3', (2, 1)),
756
(b'BzrDir.open_branchV2', (1, 13)),
757
(b'BzrDir.open_branch', None),
299
759
for verb, required_version in candidate_calls:
300
760
if required_version and medium._is_remote_before(required_version):
307
767
medium._remember_remote_is_before(required_version)
310
if verb == 'BzrDir.open_branch':
311
if response[0] != 'ok':
770
if verb == b'BzrDir.open_branch':
771
if response[0] != b'ok':
312
772
raise errors.UnexpectedSmartServerResponse(response)
313
if response[1] != '':
773
if response[1] != b'':
314
774
return ('ref', response[1])
316
return ('branch', '')
317
if response[0] not in ('ref', 'branch'):
776
return ('branch', b'')
777
if response[0] not in (b'ref', b'branch'):
318
778
raise errors.UnexpectedSmartServerResponse(response)
779
return (response[0].decode('ascii'), response[1])
321
def _get_tree_branch(self):
781
def _get_tree_branch(self, name=None):
322
782
"""See BzrDir._get_tree_branch()."""
323
return None, self.open_branch()
783
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':
785
def _open_branch(self, name, kind, location_or_format,
786
ignore_fallbacks=False, possible_transports=None):
336
788
# a branch reference, use the existing BranchReference logic.
337
789
format = BranchReferenceFormat()
338
790
return format.open(self, name=name, _found=True,
339
location=response[1], ignore_fallbacks=ignore_fallbacks)
340
branch_format_name = response[1]
791
location=location_or_format.decode('utf-8'),
792
ignore_fallbacks=ignore_fallbacks,
793
possible_transports=possible_transports)
794
branch_format_name = location_or_format
341
795
if not branch_format_name:
342
796
branch_format_name = None
343
797
format = RemoteBranchFormat(network_name=branch_format_name)
344
798
return RemoteBranch(self, self.find_repository(), format=format,
345
setup_stacking=not ignore_fallbacks, name=name)
799
setup_stacking=not ignore_fallbacks, name=name,
800
possible_transports=possible_transports)
802
def open_branch(self, name=None, unsupported=False,
803
ignore_fallbacks=False, possible_transports=None):
805
name = self._get_selected_branch()
807
raise errors.NoColocatedBranchSupport(self)
809
raise NotImplementedError('unsupported flag support not implemented yet.')
810
if self._next_open_branch_result is not None:
811
# See create_branch for details.
812
result = self._next_open_branch_result
813
self._next_open_branch_result = None
815
response = self._get_branch_reference()
816
return self._open_branch(name, response[0], response[1],
817
possible_transports=possible_transports,
818
ignore_fallbacks=ignore_fallbacks)
347
820
def _open_repo_v1(self, path):
348
verb = 'BzrDir.find_repository'
821
verb = b'BzrDir.find_repository'
349
822
response = self._call(verb, path)
350
if response[0] != 'ok':
823
if response[0] != b'ok':
351
824
raise errors.UnexpectedSmartServerResponse(response)
352
825
# servers that only support the v1 method don't support external
353
826
# references either.
354
827
self._ensure_real()
355
828
repo = self._real_bzrdir.open_repository()
356
response = response + ('no', repo._format.network_name())
829
response = response + (b'no', repo._format.network_name())
357
830
return response, repo
359
832
def _open_repo_v2(self, path):
360
verb = 'BzrDir.find_repositoryV2'
833
verb = b'BzrDir.find_repositoryV2'
361
834
response = self._call(verb, path)
362
if response[0] != 'ok':
835
if response[0] != b'ok':
363
836
raise errors.UnexpectedSmartServerResponse(response)
364
837
self._ensure_real()
365
838
repo = self._real_bzrdir.open_repository()
441
929
"""Upgrading of remote bzrdirs is not supported yet."""
444
def needs_format_conversion(self, format=None):
932
def needs_format_conversion(self, format):
445
933
"""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
936
def _get_config(self):
458
937
return RemoteBzrDirConfig(self)
461
class RemoteRepositoryFormat(repository.RepositoryFormat):
939
def _get_config_store(self):
940
return RemoteControlStore(self)
943
class RemoteInventoryTree(InventoryRevisionTree):
945
def __init__(self, repository, inv, revision_id):
946
super(RemoteInventoryTree, self).__init__(repository, inv, revision_id)
948
def archive(self, format, name, root=None, subdir=None, force_mtime=None):
949
ret = self._repository._revision_archive(
950
self.get_revision_id(), format, name, root, subdir,
951
force_mtime=force_mtime)
953
return super(RemoteInventoryTree, self).archive(
954
format, name, root, subdir, force_mtime=force_mtime)
957
def annotate_iter(self, path, file_id=None,
958
default_revision=_mod_revision.CURRENT_REVISION):
959
"""Return an iterator of revision_id, line tuples.
961
For working trees (and mutable trees in general), the special
962
revision_id 'current:' will be used for lines that are new in this
963
tree, e.g. uncommitted changes.
964
:param file_id: The file to produce an annotated version from
965
:param default_revision: For lines that don't match a basis, mark them
966
with this revision id. Not all implementations will make use of
969
ret = self._repository._annotate_file_revision(
970
self.get_revision_id(), path, file_id, default_revision)
972
return super(RemoteInventoryTree, self).annotate_iter(
973
path, file_id, default_revision=default_revision)
977
class RemoteRepositoryFormat(vf_repository.VersionedFileRepositoryFormat):
462
978
"""Format for repositories accessed over a _SmartClient.
464
980
Instances of this repository are represented by RemoteRepository
529
1067
self._custom_format.supports_tree_reference
530
1068
return self._supports_tree_reference
532
def _vfs_initialize(self, a_bzrdir, shared):
1071
def revision_graph_can_have_wrong_parents(self):
1072
if self._revision_graph_can_have_wrong_parents is None:
1074
self._revision_graph_can_have_wrong_parents = \
1075
self._custom_format.revision_graph_can_have_wrong_parents
1076
return self._revision_graph_can_have_wrong_parents
1078
def _vfs_initialize(self, a_controldir, shared):
533
1079
"""Helper for common code in initialize."""
534
1080
if self._custom_format:
535
1081
# Custom format requested
536
result = self._custom_format.initialize(a_bzrdir, shared=shared)
1082
result = self._custom_format.initialize(a_controldir, shared=shared)
537
1083
elif self._creating_bzrdir is not None:
538
1084
# Use the format that the repository we were created to back
540
1086
prior_repo = self._creating_bzrdir.open_repository()
541
1087
prior_repo._ensure_real()
542
1088
result = prior_repo._real_repository._format.initialize(
543
a_bzrdir, shared=shared)
1089
a_controldir, shared=shared)
545
1091
# assume that a_bzr is a RemoteBzrDir but the smart server didn't
546
1092
# support remote initialization.
547
1093
# We delegate to a real object at this point (as RemoteBzrDir
548
1094
# 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)
1095
# recursion if we just called a_controldir.create_repository.
1096
a_controldir._ensure_real()
1097
result = a_controldir._real_bzrdir.create_repository(shared=shared)
552
1098
if not isinstance(result, RemoteRepository):
553
return self.open(a_bzrdir)
1099
return self.open(a_controldir)
557
def initialize(self, a_bzrdir, shared=False):
1103
def initialize(self, a_controldir, shared=False):
558
1104
# 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
1105
if not isinstance(a_controldir, RemoteBzrDir):
1106
return self._vfs_initialize(a_controldir, shared)
1107
medium = a_controldir._client._medium
562
1108
if medium._is_remote_before((1, 13)):
563
return self._vfs_initialize(a_bzrdir, shared)
1109
return self._vfs_initialize(a_controldir, shared)
564
1110
# Creating on a remote bzr dir.
565
1111
# 1) get the network name to use.
566
1112
if self._custom_format:
568
1114
elif self._network_name:
569
1115
network_name = self._network_name
571
# Select the current bzrlib default and ask for that.
572
reference_bzrdir_format = bzrdir.format_registry.get('default')()
1117
# Select the current breezy default and ask for that.
1118
reference_bzrdir_format = controldir.format_registry.get('default')()
573
1119
reference_format = reference_bzrdir_format.repository_format
574
1120
network_name = reference_format.network_name()
575
1121
# 2) try direct creation via RPC
576
path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
577
verb = 'BzrDir.create_repository'
1122
path = a_controldir._path_for_remote_call(a_controldir._client)
1123
verb = b'BzrDir.create_repository'
1125
shared_str = b'True'
1127
shared_str = b'False'
583
response = a_bzrdir._call(verb, path, network_name, shared_str)
1129
response = a_controldir._call(verb, path, network_name, shared_str)
584
1130
except errors.UnknownSmartMethod:
585
1131
# Fallback - use vfs methods
586
1132
medium._remember_remote_is_before((1, 13))
587
return self._vfs_initialize(a_bzrdir, shared)
1133
return self._vfs_initialize(a_controldir, shared)
589
1135
# Turn the response into a RemoteRepository object.
590
1136
format = response_tuple_to_repo_format(response[1:])
591
1137
# Used to support creating a real format instance when needed.
592
format._creating_bzrdir = a_bzrdir
593
remote_repo = RemoteRepository(a_bzrdir, format)
1138
format._creating_bzrdir = a_controldir
1139
remote_repo = RemoteRepository(a_controldir, format)
594
1140
format._creating_repo = remote_repo
595
1141
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()
1143
def open(self, a_controldir):
1144
if not isinstance(a_controldir, RemoteBzrDir):
1145
raise AssertionError('%r is not a RemoteBzrDir' % (a_controldir,))
1146
return a_controldir.open_repository()
602
1148
def _ensure_real(self):
603
1149
if self._custom_format is None:
604
self._custom_format = repository.network_format_registry.get(
1151
self._custom_format = _mod_repository.network_format_registry.get(
1154
raise errors.UnknownFormatError(kind='repository',
1155
format=self._network_name)
608
1158
def _fetch_order(self):
910
1508
# one; unfortunately the tests rely on slightly different behaviour at
911
1509
# present -- mbp 20090710
912
1510
return (self.__class__ is other.__class__ and
913
self.bzrdir.transport.base == other.bzrdir.transport.base)
1511
self.controldir.transport.base == other.controldir.transport.base)
915
1513
def get_graph(self, other_repository=None):
916
1514
"""Return the graph for this repository format"""
917
1515
parents_provider = self._make_parents_provider(other_repository)
918
1516
return graph.Graph(parents_provider)
921
1518
def get_known_graph_ancestry(self, revision_ids):
922
1519
"""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)
1521
with self.lock_read():
1522
revision_graph = dict(((key, value) for key, value in
1523
self.get_graph().iter_ancestry(revision_ids) if value is not None))
1524
revision_graph = _mod_repository._strip_NULL_ghosts(revision_graph)
1525
return graph.KnownGraph(revision_graph)
929
1527
def gather_stats(self, revid=None, committers=None):
930
1528
"""See Repository.gather_stats()."""
931
path = self.bzrdir._path_for_remote_call(self._client)
1529
path = self.controldir._path_for_remote_call(self._client)
932
1530
# revid can be None to indicate no revisions, not just NULL_REVISION
933
if revid is None or revision.is_null(revid):
1531
if revid is None or _mod_revision.is_null(revid):
936
1534
fmt_revid = revid
937
1535
if committers is None or not committers:
938
fmt_committers = 'no'
1536
fmt_committers = b'no'
940
fmt_committers = 'yes'
1538
fmt_committers = b'yes'
941
1539
response_tuple, response_handler = self._call_expecting_body(
942
'Repository.gather_stats', path, fmt_revid, fmt_committers)
943
if response_tuple[0] != 'ok':
1540
b'Repository.gather_stats', path, fmt_revid, fmt_committers)
1541
if response_tuple[0] != b'ok':
944
1542
raise errors.UnexpectedSmartServerResponse(response_tuple)
946
1544
body = response_handler.read_body_bytes()
948
for line in body.split('\n'):
1546
for line in body.split(b'\n'):
951
key, val_text = line.split(':')
1549
key, val_text = line.split(b':')
1550
key = key.decode('ascii')
952
1551
if key in ('revisions', 'size', 'committers'):
953
1552
result[key] = int(val_text)
954
1553
elif key in ('firstrev', 'latestrev'):
955
values = val_text.split(' ')[1:]
956
result[key] = (float(values[0]), long(values[1]))
1554
values = val_text.split(b' ')[1:]
1555
result[key] = (float(values[0]), int(values[1]))
1195
1834
raise errors.UnexpectedSmartServerResponse(response)
1197
1836
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)
1837
"""Create a descendent repository for new development.
1839
Unlike clone, this does not copy the settings of the repository.
1841
with self.lock_read():
1842
dest_repo = self._create_sprouting_repo(to_bzrdir, shared=False)
1843
dest_repo.fetch(self, revision_id=revision_id)
1846
def _create_sprouting_repo(self, a_controldir, shared):
1847
if not isinstance(a_controldir._format, self.controldir._format.__class__):
1848
# use target default format.
1849
dest_repo = a_controldir.create_repository()
1851
# Most control formats need the repository to be specifically
1852
# created, but on some old all-in-one formats it's not needed
1854
dest_repo = self._format.initialize(a_controldir, shared=shared)
1855
except errors.UninitializableFormat:
1856
dest_repo = a_controldir.open_repository()
1203
1857
return dest_repo
1205
1859
### These methods are just thin shims to the VFS object for now.
1207
1861
def revision_tree(self, revision_id):
1209
return self._real_repository.revision_tree(revision_id)
1862
with self.lock_read():
1863
revision_id = _mod_revision.ensure_null(revision_id)
1864
if revision_id == _mod_revision.NULL_REVISION:
1865
return InventoryRevisionTree(self,
1866
Inventory(root_id=None), _mod_revision.NULL_REVISION)
1868
return list(self.revision_trees([revision_id]))[0]
1211
1870
def get_serializer_format(self):
1213
return self._real_repository.get_serializer_format()
1871
path = self.controldir._path_for_remote_call(self._client)
1873
response = self._call(b'VersionedFileRepository.get_serializer_format',
1875
except errors.UnknownSmartMethod:
1877
return self._real_repository.get_serializer_format()
1878
if response[0] != b'ok':
1879
raise errors.UnexpectedSmartServerResponse(response)
1215
1882
def get_commit_builder(self, branch, parents, config, timestamp=None,
1216
1883
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)
1884
revision_id=None, lossy=False):
1885
"""Obtain a CommitBuilder for this repository.
1887
:param branch: Branch to commit to.
1888
:param parents: Revision ids of the parents of the new revision.
1889
:param config: Configuration to use.
1890
:param timestamp: Optional timestamp recorded for commit.
1891
:param timezone: Optional timezone for timestamp.
1892
:param committer: Optional committer to set for commit.
1893
:param revprops: Optional dictionary of revision properties.
1894
:param revision_id: Optional revision id.
1895
:param lossy: Whether to discard data that can not be natively
1896
represented, when pushing to a foreign VCS
1898
if self._fallback_repositories and not self._format.supports_chks:
1899
raise errors.BzrError("Cannot commit directly to a stacked branch"
1900
" in pre-2a formats. See "
1901
"https://bugs.launchpad.net/bzr/+bug/375013 for details.")
1902
commit_builder_kls = vf_repository.VersionedFileCommitBuilder
1903
result = commit_builder_kls(self, parents, config,
1904
timestamp, timezone, committer, revprops, revision_id,
1906
self.start_write_group()
1227
1909
def add_fallback_repository(self, repository):
1228
1910
"""Add a repository to use for looking up data not held locally.
1272
1955
delta, new_revision_id, parents, basis_inv=basis_inv,
1273
1956
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)
1958
def add_revision(self, revision_id, rev, inv=None):
1959
_mod_revision.check_not_reserved_id(revision_id)
1960
key = (revision_id,)
1961
# check inventory present
1962
if not self.inventories.get_parent_map([key]):
1964
raise errors.WeaveRevisionNotPresent(revision_id,
1967
# yes, this is not suitable for adding with ghosts.
1968
rev.inventory_sha1 = self.add_inventory(revision_id, inv,
1971
rev.inventory_sha1 = self.inventories.get_sha1s([key])[key]
1972
self._add_revision(rev)
1974
def _add_revision(self, rev):
1975
if self._real_repository is not None:
1976
return self._real_repository._add_revision(rev)
1977
text = self._serializer.write_revision_to_string(rev)
1978
key = (rev.revision_id,)
1979
parents = tuple((parent,) for parent in rev.parent_ids)
1980
self._write_group_tokens, missing_keys = self._get_sink().insert_stream(
1981
[('revisions', [FulltextContentFactory(key, parents, None, text)])],
1982
self._format, self._write_group_tokens)
1281
1984
def get_inventory(self, revision_id):
1985
with self.lock_read():
1986
return list(self.iter_inventories([revision_id]))[0]
1988
def _iter_inventories_rpc(self, revision_ids, ordering):
1989
if ordering is None:
1990
ordering = 'unordered'
1991
path = self.controldir._path_for_remote_call(self._client)
1992
body = b"\n".join(revision_ids)
1993
response_tuple, response_handler = (
1994
self._call_with_body_bytes_expecting_body(
1995
b"VersionedFileRepository.get_inventories",
1996
(path, ordering.encode('ascii')), body))
1997
if response_tuple[0] != b"ok":
1998
raise errors.UnexpectedSmartServerResponse(response_tuple)
1999
deserializer = inventory_delta.InventoryDeltaDeserializer()
2000
byte_stream = response_handler.read_streamed_body()
2001
decoded = smart_repo._byte_stream_to_stream(byte_stream)
2003
# no results whatsoever
2005
src_format, stream = decoded
2006
if src_format.network_name() != self._format.network_name():
2007
raise AssertionError(
2008
"Mismatched RemoteRepository and stream src %r, %r" % (
2009
src_format.network_name(), self._format.network_name()))
2010
# ignore the src format, it's not really relevant
2011
prev_inv = Inventory(root_id=None,
2012
revision_id=_mod_revision.NULL_REVISION)
2013
# there should be just one substream, with inventory deltas
2015
substream_kind, substream = next(stream)
2016
except StopIteration:
2018
if substream_kind != "inventory-deltas":
2019
raise AssertionError(
2020
"Unexpected stream %r received" % substream_kind)
2021
for record in substream:
2022
(parent_id, new_id, versioned_root, tree_references, invdelta) = (
2023
deserializer.parse_text_bytes(record.get_bytes_as("fulltext")))
2024
if parent_id != prev_inv.revision_id:
2025
raise AssertionError("invalid base %r != %r" % (parent_id,
2026
prev_inv.revision_id))
2027
inv = prev_inv.create_by_apply_delta(invdelta, new_id)
2028
yield inv, inv.revision_id
2031
def _iter_inventories_vfs(self, revision_ids, ordering=None):
1282
2032
self._ensure_real()
1283
return self._real_repository.get_inventory(revision_id)
2033
return self._real_repository._iter_inventories(revision_ids, ordering)
1285
2035
def iter_inventories(self, revision_ids, ordering=None):
1287
return self._real_repository.iter_inventories(revision_ids, ordering)
2036
"""Get many inventories by revision_ids.
2038
This will buffer some or all of the texts used in constructing the
2039
inventories in memory, but will only parse a single inventory at a
2042
:param revision_ids: The expected revision ids of the inventories.
2043
:param ordering: optional ordering, e.g. 'topological'. If not
2044
specified, the order of revision_ids will be preserved (by
2045
buffering if necessary).
2046
:return: An iterator of inventories.
2048
if ((None in revision_ids)
2049
or (_mod_revision.NULL_REVISION in revision_ids)):
2050
raise ValueError('cannot get null revision inventory')
2051
for inv, revid in self._iter_inventories(revision_ids, ordering):
2053
raise errors.NoSuchRevision(self, revid)
2056
def _iter_inventories(self, revision_ids, ordering=None):
2057
if len(revision_ids) == 0:
2059
missing = set(revision_ids)
2060
if ordering is None:
2061
order_as_requested = True
2063
order = list(revision_ids)
2065
next_revid = order.pop()
2067
order_as_requested = False
2068
if ordering != 'unordered' and self._fallback_repositories:
2069
raise ValueError('unsupported ordering %r' % ordering)
2070
iter_inv_fns = [self._iter_inventories_rpc] + [
2071
fallback._iter_inventories for fallback in
2072
self._fallback_repositories]
2074
for iter_inv in iter_inv_fns:
2075
request = [revid for revid in revision_ids if revid in missing]
2076
for inv, revid in iter_inv(request, ordering):
2079
missing.remove(inv.revision_id)
2080
if ordering != 'unordered':
2084
if order_as_requested:
2085
# Yield as many results as we can while preserving order.
2086
while next_revid in invs:
2087
inv = invs.pop(next_revid)
2088
yield inv, inv.revision_id
2090
next_revid = order.pop()
2092
# We still want to fully consume the stream, just
2093
# in case it is not actually finished at this point
2096
except errors.UnknownSmartMethod:
2097
for inv, revid in self._iter_inventories_vfs(revision_ids, ordering):
2101
if order_as_requested:
2102
if next_revid is not None:
2103
yield None, next_revid
2106
yield invs.get(revid), revid
2109
yield None, missing.pop()
1290
2111
def get_revision(self, revision_id):
1292
return self._real_repository.get_revision(revision_id)
2112
with self.lock_read():
2113
return self.get_revisions([revision_id])[0]
1294
2115
def get_transaction(self):
1295
2116
self._ensure_real()
1296
2117
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)
2119
def clone(self, a_controldir, revision_id=None):
2120
with self.lock_read():
2121
dest_repo = self._create_sprouting_repo(
2122
a_controldir, shared=self.is_shared())
2123
self.copy_content_into(dest_repo, revision_id)
1303
2126
def make_working_trees(self):
1304
2127
"""See Repository.make_working_trees"""
1306
return self._real_repository.make_working_trees()
2128
path = self.controldir._path_for_remote_call(self._client)
2130
response = self._call(b'Repository.make_working_trees', path)
2131
except errors.UnknownSmartMethod:
2133
return self._real_repository.make_working_trees()
2134
if response[0] not in (b'yes', b'no'):
2135
raise SmartProtocolError('unexpected response code %s' % (response,))
2136
return response[0] == b'yes'
1308
2138
def refresh_data(self):
1309
"""Re-read any data needed to to synchronise with disk.
2139
"""Re-read any data needed to synchronise with disk.
1311
2141
This method is intended to be called after another repository instance
1312
2142
(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.
2143
repository. On all repositories this will work outside of write groups.
2144
Some repository formats (pack and newer for breezy native formats)
2145
support refresh_data inside write groups. If called inside a write
2146
group on a repository that does not support refreshing in a write group
2147
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
2149
if self._real_repository is not None:
1320
2150
self._real_repository.refresh_data()
2151
# Refresh the parents cache for this object
2152
self._unstacked_provider.disable_cache()
2153
self._unstacked_provider.enable_cache()
1322
2155
def revision_ids_to_search_result(self, result_set):
1323
2156
"""Convert a set of revision ids to a graph SearchResult."""
1324
2157
result_parents = set()
1325
for parents in self.get_graph().get_parent_map(
1326
result_set).itervalues():
2158
for parents in viewvalues(self.get_graph().get_parent_map(result_set)):
1327
2159
result_parents.update(parents)
1328
2160
included_keys = result_set.intersection(result_parents)
1329
2161
start_keys = result_set.difference(included_keys)
1330
2162
exclude_keys = result_parents.difference(result_set)
1331
result = graph.SearchResult(start_keys, exclude_keys,
2163
result = vf_search.SearchResult(start_keys, exclude_keys,
1332
2164
len(result_set), result_set)
1336
def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
2167
def search_missing_revision_ids(self, other,
2168
find_ghosts=True, revision_ids=None, if_present_ids=None,
1337
2170
"""Return the revision ids that other has that this does not.
1339
2172
These are returned in topological order.
1341
2174
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)
2176
with self.lock_read():
2177
inter_repo = _mod_repository.InterRepository.get(other, self)
2178
return inter_repo.search_missing_revision_ids(
2179
find_ghosts=find_ghosts, revision_ids=revision_ids,
2180
if_present_ids=if_present_ids, limit=limit)
1346
def fetch(self, source, revision_id=None, pb=None, find_ghosts=False,
2182
def fetch(self, source, revision_id=None, find_ghosts=False,
1347
2183
fetch_spec=None):
1348
2184
# No base implementation to use as RemoteRepository is not a subclass
1349
2185
# of Repository; so this is a copy of Repository.fetch().
1388
2223
return self._real_repository._get_versioned_file_checker(
1389
2224
revisions, revision_versions_cache)
2226
def _iter_files_bytes_rpc(self, desired_files, absent):
2227
path = self.controldir._path_for_remote_call(self._client)
2230
for (file_id, revid, identifier) in desired_files:
2231
lines.append(b''.join([
2232
osutils.safe_file_id(file_id),
2234
osutils.safe_revision_id(revid)]))
2235
identifiers.append(identifier)
2236
(response_tuple, response_handler) = (
2237
self._call_with_body_bytes_expecting_body(
2238
b"Repository.iter_files_bytes", (path, ), b"\n".join(lines)))
2239
if response_tuple != (b'ok', ):
2240
response_handler.cancel_read_body()
2241
raise errors.UnexpectedSmartServerResponse(response_tuple)
2242
byte_stream = response_handler.read_streamed_body()
2243
def decompress_stream(start, byte_stream, unused):
2244
decompressor = zlib.decompressobj()
2245
yield decompressor.decompress(start)
2246
while decompressor.unused_data == b"":
2248
data = next(byte_stream)
2249
except StopIteration:
2251
yield decompressor.decompress(data)
2252
yield decompressor.flush()
2253
unused.append(decompressor.unused_data)
2256
while not b"\n" in unused:
2258
unused += next(byte_stream)
2259
except StopIteration:
2261
header, rest = unused.split(b"\n", 1)
2262
args = header.split(b"\0")
2263
if args[0] == b"absent":
2264
absent[identifiers[int(args[3])]] = (args[1], args[2])
2267
elif args[0] == b"ok":
2270
raise errors.UnexpectedSmartServerResponse(args)
2272
yield (identifiers[idx],
2273
decompress_stream(rest, byte_stream, unused_chunks))
2274
unused = b"".join(unused_chunks)
1391
2276
def iter_files_bytes(self, desired_files):
1392
2277
"""See Repository.iter_file_bytes.
1395
return self._real_repository.iter_files_bytes(desired_files)
2281
for (identifier, bytes_iterator) in self._iter_files_bytes_rpc(
2282
desired_files, absent):
2283
yield identifier, bytes_iterator
2284
for fallback in self._fallback_repositories:
2287
desired_files = [(key[0], key[1], identifier)
2288
for identifier, key in viewitems(absent)]
2289
for (identifier, bytes_iterator) in fallback.iter_files_bytes(desired_files):
2290
del absent[identifier]
2291
yield identifier, bytes_iterator
2293
# There may be more missing items, but raise an exception
2295
missing_identifier = next(iter(absent))
2296
missing_key = absent[missing_identifier]
2297
raise errors.RevisionNotPresent(revision_id=missing_key[1],
2298
file_id=missing_key[0])
2299
except errors.UnknownSmartMethod:
2301
for (identifier, bytes_iterator) in (
2302
self._real_repository.iter_files_bytes(desired_files)):
2303
yield identifier, bytes_iterator
2305
def get_cached_parent_map(self, revision_ids):
2306
"""See breezy.CachingParentsProvider.get_cached_parent_map"""
2307
return self._unstacked_provider.get_cached_parent_map(revision_ids)
1397
2309
def get_parent_map(self, revision_ids):
1398
"""See bzrlib.Graph.get_parent_map()."""
2310
"""See breezy.Graph.get_parent_map()."""
1399
2311
return self._make_parents_provider().get_parent_map(revision_ids)
1401
2313
def _get_parent_map_rpc(self, keys):
1529
2430
revision_graph[d[0]] = (NULL_REVISION,)
1530
2431
return revision_graph
1533
2433
def get_signature_text(self, revision_id):
1535
return self._real_repository.get_signature_text(revision_id)
2434
with self.lock_read():
2435
path = self.controldir._path_for_remote_call(self._client)
2437
response_tuple, response_handler = self._call_expecting_body(
2438
b'Repository.get_revision_signature_text', path, revision_id)
2439
except errors.UnknownSmartMethod:
2441
return self._real_repository.get_signature_text(revision_id)
2442
except errors.NoSuchRevision as err:
2443
for fallback in self._fallback_repositories:
2445
return fallback.get_signature_text(revision_id)
2446
except errors.NoSuchRevision:
2450
if response_tuple[0] != b'ok':
2451
raise errors.UnexpectedSmartServerResponse(response_tuple)
2452
return response_handler.read_body_bytes()
1538
2454
def _get_inventory_xml(self, revision_id):
1540
return self._real_repository._get_inventory_xml(revision_id)
2455
with self.lock_read():
2456
# This call is used by older working tree formats,
2457
# which stored a serialized basis inventory.
2459
return self._real_repository._get_inventory_xml(revision_id)
1542
2461
def reconcile(self, other=None, thorough=False):
1544
return self._real_repository.reconcile(other=other, thorough=thorough)
2462
from ..reconcile import RepoReconciler
2463
with self.lock_write():
2464
path = self.controldir._path_for_remote_call(self._client)
2466
response, handler = self._call_expecting_body(
2467
b'Repository.reconcile', path, self._lock_token)
2468
except (errors.UnknownSmartMethod, errors.TokenLockingNotSupported):
2470
return self._real_repository.reconcile(other=other, thorough=thorough)
2471
if response != (b'ok', ):
2472
raise errors.UnexpectedSmartServerResponse(response)
2473
body = handler.read_body_bytes()
2474
result = RepoReconciler(self)
2475
for line in body.split(b'\n'):
2478
key, val_text = line.split(b':')
2479
if key == b"garbage_inventories":
2480
result.garbage_inventories = int(val_text)
2481
elif key == b"inconsistent_parents":
2482
result.inconsistent_parents = int(val_text)
2484
mutter("unknown reconcile key %r" % key)
1546
2487
def all_revision_ids(self):
1548
return self._real_repository.all_revision_ids()
2488
path = self.controldir._path_for_remote_call(self._client)
2490
response_tuple, response_handler = self._call_expecting_body(
2491
b"Repository.all_revision_ids", path)
2492
except errors.UnknownSmartMethod:
2494
return self._real_repository.all_revision_ids()
2495
if response_tuple != (b"ok", ):
2496
raise errors.UnexpectedSmartServerResponse(response_tuple)
2497
revids = set(response_handler.read_body_bytes().splitlines())
2498
for fallback in self._fallback_repositories:
2499
revids.update(set(fallback.all_revision_ids()))
2502
def _filtered_revision_trees(self, revision_ids, file_ids):
2503
"""Return Tree for a revision on this branch with only some files.
2505
:param revision_ids: a sequence of revision-ids;
2506
a revision-id may not be None or b'null:'
2507
:param file_ids: if not None, the result is filtered
2508
so that only those file-ids, their parents and their
2509
children are included.
2511
inventories = self.iter_inventories(revision_ids)
2512
for inv in inventories:
2513
# Should we introduce a FilteredRevisionTree class rather
2514
# than pre-filter the inventory here?
2515
filtered_inv = inv.filter(file_ids)
2516
yield InventoryRevisionTree(self, filtered_inv, filtered_inv.revision_id)
1551
2518
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)
2519
with self.lock_read():
2520
medium = self._client._medium
2521
if medium._is_remote_before((1, 2)):
2523
for delta in self._real_repository.get_deltas_for_revisions(
2524
revisions, specific_fileids):
2527
# Get the revision-ids of interest
2528
required_trees = set()
2529
for revision in revisions:
2530
required_trees.add(revision.revision_id)
2531
required_trees.update(revision.parent_ids[:1])
2533
# Get the matching filtered trees. Note that it's more
2534
# efficient to pass filtered trees to changes_from() rather
2535
# than doing the filtering afterwards. changes_from() could
2536
# arguably do the filtering itself but it's path-based, not
2537
# file-id based, so filtering before or afterwards is
2539
if specific_fileids is None:
2540
trees = dict((t.get_revision_id(), t) for
2541
t in self.revision_trees(required_trees))
2543
trees = dict((t.get_revision_id(), t) for
2544
t in self._filtered_revision_trees(required_trees,
2547
# Calculate the deltas
2548
for revision in revisions:
2549
if not revision.parent_ids:
2550
old_tree = self.revision_tree(_mod_revision.NULL_REVISION)
2552
old_tree = trees[revision.parent_ids[0]]
2553
yield trees[revision.revision_id].changes_from(old_tree)
1557
2555
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)
2556
with self.lock_read():
2557
r = self.get_revision(revision_id)
2558
return list(self.get_deltas_for_revisions([r],
2559
specific_fileids=specific_fileids))[0]
1563
2561
def revision_trees(self, revision_ids):
1565
return self._real_repository.revision_trees(revision_ids)
2562
with self.lock_read():
2563
inventories = self.iter_inventories(revision_ids)
2564
for inv in inventories:
2565
yield RemoteInventoryTree(self, inv, inv.revision_id)
1568
2567
def get_revision_reconcile(self, revision_id):
1570
return self._real_repository.get_revision_reconcile(revision_id)
2568
with self.lock_read():
2570
return self._real_repository.get_revision_reconcile(revision_id)
1573
2572
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)
2573
with self.lock_read():
2575
return self._real_repository.check(revision_ids=revision_ids,
2576
callback_refs=callback_refs, check_repo=check_repo)
1578
2578
def copy_content_into(self, destination, revision_id=None):
1580
return self._real_repository.copy_content_into(
1581
destination, revision_id=revision_id)
2579
"""Make a complete copy of the content in self into destination.
2581
This is a destructive operation! Do not use it on existing
2584
interrepo = _mod_repository.InterRepository.get(self, destination)
2585
return interrepo.copy_content(revision_id)
1583
2587
def _copy_repository_tarball(self, to_bzrdir, revision_id=None):
1584
2588
# get a tarball of the remote repository, and copy from that into the
1586
from bzrlib import osutils
1588
2591
# TODO: Maybe a progress bar while streaming the tarball?
1589
note("Copying repository content as tarball...")
2592
note(gettext("Copying repository content as tarball..."))
1590
2593
tar_file = self._get_tarball('bz2')
1591
2594
if tar_file is None:
1680
2693
self._ensure_real()
1681
2694
return self._real_repository.texts
1684
def get_revisions(self, revision_ids):
1686
return self._real_repository.get_revisions(revision_ids)
2696
def _iter_revisions_rpc(self, revision_ids):
2697
body = b"\n".join(revision_ids)
2698
path = self.controldir._path_for_remote_call(self._client)
2699
response_tuple, response_handler = (
2700
self._call_with_body_bytes_expecting_body(
2701
b"Repository.iter_revisions", (path, ), body))
2702
if response_tuple[0] != b"ok":
2703
raise errors.UnexpectedSmartServerResponse(response_tuple)
2704
serializer_format = response_tuple[1].decode('ascii')
2705
serializer = serializer_format_registry.get(serializer_format)
2706
byte_stream = response_handler.read_streamed_body()
2707
decompressor = zlib.decompressobj()
2709
for bytes in byte_stream:
2710
chunks.append(decompressor.decompress(bytes))
2711
if decompressor.unused_data != b"":
2712
chunks.append(decompressor.flush())
2713
yield serializer.read_revision_from_string(b"".join(chunks))
2714
unused = decompressor.unused_data
2715
decompressor = zlib.decompressobj()
2716
chunks = [decompressor.decompress(unused)]
2717
chunks.append(decompressor.flush())
2718
text = b"".join(chunks)
2720
yield serializer.read_revision_from_string(b"".join(chunks))
2722
def iter_revisions(self, revision_ids):
2723
for rev_id in revision_ids:
2724
if not rev_id or not isinstance(rev_id, bytes):
2725
raise errors.InvalidRevisionId(
2726
revision_id=rev_id, branch=self)
2727
with self.lock_read():
2729
missing = set(revision_ids)
2730
for rev in self._iter_revisions_rpc(revision_ids):
2731
missing.remove(rev.revision_id)
2732
yield (rev.revision_id, rev)
2733
for fallback in self._fallback_repositories:
2736
for (revid, rev) in fallback.iter_revisions(missing):
2739
missing.remove(revid)
2740
for revid in missing:
2742
except errors.UnknownSmartMethod:
2744
for entry in self._real_repository.iter_revisions(revision_ids):
1688
2747
def supports_rich_root(self):
1689
2748
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
2751
def _serializer(self):
1697
2752
return self._format._serializer
1699
2754
def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
1701
return self._real_repository.store_revision_signature(
1702
gpg_strategy, plaintext, revision_id)
2755
with self.lock_write():
2756
signature = gpg_strategy.sign(plaintext, gpg.MODE_CLEAR)
2757
self.add_signature_text(revision_id, signature)
1704
2759
def add_signature_text(self, revision_id, signature):
1706
return self._real_repository.add_signature_text(revision_id, signature)
2760
if self._real_repository:
2761
# If there is a real repository the write group will
2762
# be in the real repository as well, so use that:
2764
return self._real_repository.add_signature_text(
2765
revision_id, signature)
2766
path = self.controldir._path_for_remote_call(self._client)
2767
response, handler = self._call_with_body_bytes_expecting_body(
2768
b'Repository.add_signature_text', (path, self._lock_token,
2770
tuple([token.encode('utf-8') for token in self._write_group_tokens]),
2772
handler.cancel_read_body()
2774
if response[0] != b'ok':
2775
raise errors.UnexpectedSmartServerResponse(response)
2776
self._write_group_tokens = [token.decode('utf-8') for token in response[1:]]
1708
2778
def has_signature_for_revision_id(self, revision_id):
1710
return self._real_repository.has_signature_for_revision_id(revision_id)
2779
path = self.controldir._path_for_remote_call(self._client)
2781
response = self._call(b'Repository.has_signature_for_revision_id',
2783
except errors.UnknownSmartMethod:
2785
return self._real_repository.has_signature_for_revision_id(
2787
if response[0] not in (b'yes', b'no'):
2788
raise SmartProtocolError('unexpected response code %s' % (response,))
2789
if response[0] == b'yes':
2791
for fallback in self._fallback_repositories:
2792
if fallback.has_signature_for_revision_id(revision_id):
2796
def verify_revision_signature(self, revision_id, gpg_strategy):
2797
with self.lock_read():
2798
if not self.has_signature_for_revision_id(revision_id):
2799
return gpg.SIGNATURE_NOT_SIGNED, None
2800
signature = self.get_signature_text(revision_id)
2802
testament = _mod_testament.Testament.from_revision(self, revision_id)
2804
(status, key, signed_plaintext) = gpg_strategy.verify(signature)
2805
if testament.as_short_text() != signed_plaintext:
2806
return gpg.SIGNATURE_NOT_VALID, None
2807
return (status, key)
1712
2809
def item_keys_introduced_by(self, revision_ids, _files_pb=None):
1713
2810
self._ensure_real()
1714
2811
return self._real_repository.item_keys_introduced_by(revision_ids,
1715
2812
_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
2814
def _find_inconsistent_revision_parents(self, revisions_iterator=None):
1723
2815
self._ensure_real()
1724
2816
return self._real_repository._find_inconsistent_revision_parents(
1742
2833
:param recipe: A search recipe (start, stop, count).
1743
2834
: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))
2836
start_keys = b' '.join(recipe[1])
2837
stop_keys = b' '.join(recipe[2])
2838
count = str(recipe[3]).encode('ascii')
2839
return b'\n'.join((start_keys, stop_keys, count))
1750
2841
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)
2842
parts = search_result.get_network_struct()
2843
return b'\n'.join(parts)
1759
2845
def autopack(self):
1760
path = self.bzrdir._path_for_remote_call(self._client)
2846
path = self.controldir._path_for_remote_call(self._client)
1762
response = self._call('PackRepository.autopack', path)
2848
response = self._call(b'PackRepository.autopack', path)
1763
2849
except errors.UnknownSmartMethod:
1764
2850
self._ensure_real()
1765
2851
self._real_repository._pack_collection.autopack()
1767
2853
self.refresh_data()
1768
if response[0] != 'ok':
1769
raise errors.UnexpectedSmartServerResponse(response)
1772
class RemoteStreamSink(repository.StreamSink):
2854
if response[0] != b'ok':
2855
raise errors.UnexpectedSmartServerResponse(response)
2857
def _revision_archive(self, revision_id, format, name, root, subdir,
2859
path = self.controldir._path_for_remote_call(self._client)
2860
format = format or ''
2862
subdir = subdir or ''
2863
force_mtime = int(force_mtime) if force_mtime is not None else None
2865
response, protocol = self._call_expecting_body(
2866
b'Repository.revision_archive', path,
2868
format.encode('ascii'),
2869
os.path.basename(name).encode('utf-8'),
2870
root.encode('utf-8'),
2871
subdir.encode('utf-8'),
2873
except errors.UnknownSmartMethod:
2875
if response[0] == b'ok':
2876
return iter([protocol.read_body_bytes()])
2877
raise errors.UnexpectedSmartServerResponse(response)
2879
def _annotate_file_revision(self, revid, tree_path, file_id, default_revision):
2880
path = self.controldir._path_for_remote_call(self._client)
2881
tree_path = tree_path.encode('utf-8')
2882
file_id = file_id or b''
2883
default_revision = default_revision or b''
2885
response, handler = self._call_expecting_body(
2886
b'Repository.annotate_file_revision', path,
2887
revid, tree_path, file_id, default_revision)
2888
except errors.UnknownSmartMethod:
2890
if response[0] != b'ok':
2891
raise errors.UnexpectedSmartServerResponse(response)
2892
return map(tuple, bencode.bdecode(handler.read_body_bytes()))
2895
class RemoteStreamSink(vf_repository.StreamSink):
1774
2897
def _insert_real(self, stream, src_format, resume_tokens):
1775
2898
self.target_repo._ensure_real()
2059
3238
def network_name(self):
2060
3239
return self._network_name
2062
def open(self, a_bzrdir, name=None, ignore_fallbacks=False):
2063
return a_bzrdir.open_branch(name=name,
3241
def open(self, a_controldir, name=None, ignore_fallbacks=False):
3242
return a_controldir.open_branch(name=name,
2064
3243
ignore_fallbacks=ignore_fallbacks)
2066
def _vfs_initialize(self, a_bzrdir, name):
3245
def _vfs_initialize(self, a_controldir, name, append_revisions_only,
2067
3247
# Initialisation when using a local bzrdir object, or a non-vfs init
2068
3248
# method is not available on the server.
2069
3249
# 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,
3251
if isinstance(a_controldir, RemoteBzrDir):
3252
a_controldir._ensure_real()
3253
result = self._custom_format.initialize(a_controldir._real_bzrdir,
3254
name=name, append_revisions_only=append_revisions_only,
3255
repository=repository)
2076
3257
# 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
3258
result = self._custom_format.initialize(a_controldir, name=name,
3259
append_revisions_only=append_revisions_only,
3260
repository=repository)
3261
if (isinstance(a_controldir, RemoteBzrDir) and
2079
3262
not isinstance(result, RemoteBranch)):
2080
result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result,
3263
result = RemoteBranch(a_controldir, a_controldir.find_repository(), result,
2084
def initialize(self, a_bzrdir, name=None):
3267
def initialize(self, a_controldir, name=None, repository=None,
3268
append_revisions_only=None):
3270
name = a_controldir._get_selected_branch()
2085
3271
# 1) get the network name to use.
2086
3272
if self._custom_format:
2087
3273
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')()
3275
# Select the current breezy default and ask for that.
3276
reference_bzrdir_format = controldir.format_registry.get('default')()
2091
3277
reference_format = reference_bzrdir_format.get_branch_format()
2092
3278
self._custom_format = reference_format
2093
3279
network_name = reference_format.network_name()
2094
3280
# 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
3281
if not isinstance(a_controldir, RemoteBzrDir):
3282
return self._vfs_initialize(a_controldir, name=name,
3283
append_revisions_only=append_revisions_only,
3284
repository=repository)
3285
medium = a_controldir._client._medium
2098
3286
if medium._is_remote_before((1, 13)):
2099
return self._vfs_initialize(a_bzrdir, name=name)
3287
return self._vfs_initialize(a_controldir, name=name,
3288
append_revisions_only=append_revisions_only,
3289
repository=repository)
2100
3290
# Creating on a remote bzr dir.
2101
3291
# 2) try direct creation via RPC
2102
path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
2103
if name is not None:
3292
path = a_controldir._path_for_remote_call(a_controldir._client)
2104
3294
# XXX JRV20100304: Support creating colocated branches
2105
3295
raise errors.NoColocatedBranchSupport(self)
2106
verb = 'BzrDir.create_branch'
3296
verb = b'BzrDir.create_branch'
2108
response = a_bzrdir._call(verb, path, network_name)
3298
response = a_controldir._call(verb, path, network_name)
2109
3299
except errors.UnknownSmartMethod:
2110
3300
# Fallback - use vfs methods
2111
3301
medium._remember_remote_is_before((1, 13))
2112
return self._vfs_initialize(a_bzrdir, name=name)
2113
if response[0] != 'ok':
3302
return self._vfs_initialize(a_controldir, name=name,
3303
append_revisions_only=append_revisions_only,
3304
repository=repository)
3305
if response[0] != b'ok':
2114
3306
raise errors.UnexpectedSmartServerResponse(response)
2115
3307
# Turn the response into a RemoteRepository object.
2116
3308
format = RemoteBranchFormat(network_name=response[1])
2117
3309
repo_format = response_tuple_to_repo_format(response[3:])
2118
if response[2] == '':
2119
repo_bzrdir = a_bzrdir
3310
repo_path = response[2].decode('utf-8')
3311
if repository is not None:
3312
remote_repo_url = urlutils.join(a_controldir.user_url, repo_path)
3313
url_diff = urlutils.relative_url(repository.user_url,
3316
raise AssertionError(
3317
'repository.user_url %r does not match URL from server '
3318
'response (%r + %r)'
3319
% (repository.user_url, a_controldir.user_url, repo_path))
3320
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,
3323
repo_bzrdir = a_controldir
3325
repo_bzrdir = RemoteBzrDir(
3326
a_controldir.root_transport.clone(repo_path), a_controldir._format,
3327
a_controldir._client)
3328
remote_repo = RemoteRepository(repo_bzrdir, repo_format)
3329
remote_branch = RemoteBranch(a_controldir, remote_repo,
2126
3330
format=format, setup_stacking=False, name=name)
3331
if append_revisions_only:
3332
remote_branch.set_append_revisions_only(append_revisions_only)
2127
3333
# XXX: We know this is a new branch, so it must have revno 0, revid
2128
3334
# NULL_REVISION. Creating the branch locked would make this be unable
2129
3335
# to be wrong; here its simply very unlikely to be wrong. RBC 20090225
2148
3354
self._ensure_real()
2149
3355
return self._custom_format.supports_set_append_revisions_only()
3357
def _use_default_local_heads_to_fetch(self):
3358
# If the branch format is a metadir format *and* its heads_to_fetch
3359
# implementation is not overridden vs the base class, we can use the
3360
# base class logic rather than use the heads_to_fetch RPC. This is
3361
# usually cheaper in terms of net round trips, as the last-revision and
3362
# tags info fetched is cached and would be fetched anyway.
3364
if isinstance(self._custom_format, bzrbranch.BranchFormatMetadir):
3365
branch_class = self._custom_format._branch_class()
3366
heads_to_fetch_impl = get_unbound_function(branch_class.heads_to_fetch)
3367
if heads_to_fetch_impl is get_unbound_function(branch.Branch.heads_to_fetch):
3372
class RemoteBranchStore(_mod_config.IniFileStore):
3373
"""Branch store which attempts to use HPSS calls to retrieve branch store.
3375
Note that this is specific to bzr-based formats.
3378
def __init__(self, branch):
3379
super(RemoteBranchStore, self).__init__()
3380
self.branch = branch
3382
self._real_store = None
3384
def external_url(self):
3385
return urlutils.join(self.branch.user_url, 'branch.conf')
3387
def _load_content(self):
3388
path = self.branch._remote_path()
3390
response, handler = self.branch._call_expecting_body(
3391
b'Branch.get_config_file', path)
3392
except errors.UnknownSmartMethod:
3394
return self._real_store._load_content()
3395
if len(response) and response[0] != b'ok':
3396
raise errors.UnexpectedSmartServerResponse(response)
3397
return handler.read_body_bytes()
3399
def _save_content(self, content):
3400
path = self.branch._remote_path()
3402
response, handler = self.branch._call_with_body_bytes_expecting_body(
3403
b'Branch.put_config_file', (path,
3404
self.branch._lock_token, self.branch._repo_lock_token),
3406
except errors.UnknownSmartMethod:
3408
return self._real_store._save_content(content)
3409
handler.cancel_read_body()
3410
if response != (b'ok', ):
3411
raise errors.UnexpectedSmartServerResponse(response)
3413
def _ensure_real(self):
3414
self.branch._ensure_real()
3415
if self._real_store is None:
3416
self._real_store = _mod_config.BranchStore(self.branch)
2152
3419
class RemoteBranch(branch.Branch, _RpcHelper, lock._RelockDebugMixin):
2153
3420
"""Branch stored on a server accessed by HPSS RPC.
2644
3963
self._ensure_real()
2645
3964
return self._real_branch._set_parent_location(url)
2648
3966
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)
3968
with self.lock_write():
3969
self._clear_cached_state_of_remote_branch_only()
3971
return self._real_branch.pull(
3972
source, overwrite=overwrite, stop_revision=stop_revision,
3973
_override_hook_target=self, **kwargs)
3975
def push(self, target, overwrite=False, stop_revision=None, lossy=False):
3976
with self.lock_read():
3978
return self._real_branch.push(
3979
target, overwrite=overwrite, stop_revision=stop_revision, lossy=lossy,
3980
_override_hook_source_branch=self)
3982
def peek_lock_mode(self):
3983
return self._lock_mode
2663
3985
def is_locked(self):
2664
3986
return self._lock_count >= 1
3988
def revision_id_to_dotted_revno(self, revision_id):
3989
"""Given a revision id, return its dotted revno.
3991
:return: a tuple like (1,) or (400,1,3).
3993
with self.lock_read():
3995
response = self._call(b'Branch.revision_id_to_revno',
3996
self._remote_path(), revision_id)
3997
except errors.UnknownSmartMethod:
3999
return self._real_branch.revision_id_to_dotted_revno(revision_id)
4000
if response[0] == b'ok':
4001
return tuple([int(x) for x in response[1:]])
4003
raise errors.UnexpectedSmartServerResponse(response)
2667
4005
def revision_id_to_revno(self, revision_id):
2669
return self._real_branch.revision_id_to_revno(revision_id)
4006
"""Given a revision id on the branch mainline, return its revno.
4010
with self.lock_read():
4012
response = self._call(b'Branch.revision_id_to_revno',
4013
self._remote_path(), revision_id)
4014
except errors.UnknownSmartMethod:
4016
return self._real_branch.revision_id_to_revno(revision_id)
4017
if response[0] == b'ok':
4018
if len(response) == 2:
4019
return int(response[1])
4020
raise NoSuchRevision(self, revision_id)
4022
raise errors.UnexpectedSmartServerResponse(response)
2672
4024
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)
4025
with self.lock_write():
4026
# XXX: These should be returned by the set_last_revision_info verb
4027
old_revno, old_revid = self.last_revision_info()
4028
self._run_pre_change_branch_tip_hooks(revno, revision_id)
4029
if not revision_id or not isinstance(revision_id, bytes):
4030
raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
4032
response = self._call(b'Branch.set_last_revision_info',
4033
self._remote_path(), self._lock_token, self._repo_lock_token,
4034
str(revno).encode('ascii'), revision_id)
4035
except errors.UnknownSmartMethod:
4037
self._clear_cached_state_of_remote_branch_only()
4038
self._real_branch.set_last_revision_info(revno, revision_id)
4039
self._last_revision_info_cache = revno, revision_id
4041
if response == (b'ok',):
4042
self._clear_cached_state()
4043
self._last_revision_info_cache = revno, revision_id
4044
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
4045
# Update the _real_branch's cache too.
4046
if self._real_branch is not None:
4047
cache = self._last_revision_info_cache
4048
self._real_branch._last_revision_info_cache = cache
4050
raise errors.UnexpectedSmartServerResponse(response)
2699
4052
def generate_revision_history(self, revision_id, last_rev=None,
2700
4053
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))
4054
with self.lock_write():
4055
medium = self._client._medium
4056
if not medium._is_remote_before((1, 6)):
4057
# Use a smart method for 1.6 and above servers
4059
self._set_last_revision_descendant(revision_id, other_branch,
4060
allow_diverged=True, allow_overwrite_descendant=True)
4062
except errors.UnknownSmartMethod:
4063
medium._remember_remote_is_before((1, 6))
4064
self._clear_cached_state_of_remote_branch_only()
4065
graph = self.repository.get_graph()
4066
(last_revno, last_revid) = self.last_revision_info()
4067
known_revision_ids = [
4068
(last_revid, last_revno),
4069
(_mod_revision.NULL_REVISION, 0),
4071
if last_rev is not None:
4072
if not graph.is_ancestor(last_rev, revision_id):
4073
# our previous tip is not merged into stop_revision
4074
raise errors.DivergedBranches(self, other_branch)
4075
revno = graph.find_distance_to_null(revision_id, known_revision_ids)
4076
self.set_last_revision_info(revno, revision_id)
2714
4078
def set_push_location(self, location):
4079
self._set_config_location('push_location', location)
4081
def heads_to_fetch(self):
4082
if self._format._use_default_local_heads_to_fetch():
4083
# We recognise this format, and its heads-to-fetch implementation
4084
# is the default one (tip + tags). In this case it's cheaper to
4085
# just use the default implementation rather than a special RPC as
4086
# the tip and tags data is cached.
4087
return branch.Branch.heads_to_fetch(self)
4088
medium = self._client._medium
4089
if medium._is_remote_before((2, 4)):
4090
return self._vfs_heads_to_fetch()
4092
return self._rpc_heads_to_fetch()
4093
except errors.UnknownSmartMethod:
4094
medium._remember_remote_is_before((2, 4))
4095
return self._vfs_heads_to_fetch()
4097
def _rpc_heads_to_fetch(self):
4098
response = self._call(b'Branch.heads_to_fetch', self._remote_path())
4099
if len(response) != 2:
4100
raise errors.UnexpectedSmartServerResponse(response)
4101
must_fetch, if_present_fetch = response
4102
return set(must_fetch), set(if_present_fetch)
4104
def _vfs_heads_to_fetch(self):
2715
4105
self._ensure_real()
2716
return self._real_branch.set_push_location(location)
4106
return self._real_branch.heads_to_fetch()
2719
4109
class RemoteConfig(object):
2774
4174
medium = self._branch._client._medium
2775
4175
if medium._is_remote_before((1, 14)):
2776
4176
return self._vfs_set_option(value, name, section)
4177
if isinstance(value, dict):
4178
if medium._is_remote_before((2, 2)):
4179
return self._vfs_set_option(value, name, section)
4180
return self._set_config_option_dict(value, name, section)
4182
return self._set_config_option(value, name, section)
4184
def _set_config_option(self, value, name, section):
2778
4186
path = self._branch._remote_path()
2779
response = self._branch._client.call('Branch.set_config_option',
4187
response = self._branch._client.call(b'Branch.set_config_option',
2780
4188
path, self._branch._lock_token, self._branch._repo_lock_token,
2781
value.encode('utf8'), name, section or '')
4189
value.encode('utf8'), name.encode('utf-8'),
4190
(section or '').encode('utf-8'))
2782
4191
except errors.UnknownSmartMethod:
4192
medium = self._branch._client._medium
2783
4193
medium._remember_remote_is_before((1, 14))
2784
4194
return self._vfs_set_option(value, name, section)
2785
4195
if response != ():
2786
4196
raise errors.UnexpectedSmartServerResponse(response)
4198
def _serialize_option_dict(self, option_dict):
4200
for key, value in option_dict.items():
4201
if isinstance(key, text_type):
4202
key = key.encode('utf8')
4203
if isinstance(value, text_type):
4204
value = value.encode('utf8')
4205
utf8_dict[key] = value
4206
return bencode.bencode(utf8_dict)
4208
def _set_config_option_dict(self, value, name, section):
4210
path = self._branch._remote_path()
4211
serialised_dict = self._serialize_option_dict(value)
4212
response = self._branch._client.call(
4213
b'Branch.set_config_option_dict',
4214
path, self._branch._lock_token, self._branch._repo_lock_token,
4215
serialised_dict, name.encode('utf-8'), (section or '').encode('utf-8'))
4216
except errors.UnknownSmartMethod:
4217
medium = self._branch._client._medium
4218
medium._remember_remote_is_before((2, 2))
4219
return self._vfs_set_option(value, name, section)
4221
raise errors.UnexpectedSmartServerResponse(response)
2788
4223
def _real_object(self):
2789
4224
self._branch._ensure_real()
2790
4225
return self._branch._real_branch
2867
4296
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)
4299
return err.error_args[0].decode('utf-8')
4301
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
4303
if not isinstance(err.error_verb, bytes):
4304
raise TypeError(err.error_verb)
4306
translator = error_translators.get(err.error_verb)
4310
raise translator(err, find, get_path)
4312
translator = no_context_error_translators.get(err.error_verb)
4314
raise errors.UnknownErrorFromSmartServer(err)
4316
raise translator(err)
4319
error_translators.register(b'NoSuchRevision',
4320
lambda err, find, get_path: NoSuchRevision(
4321
find('branch'), err.error_args[0]))
4322
error_translators.register(b'nosuchrevision',
4323
lambda err, find, get_path: NoSuchRevision(
4324
find('repository'), err.error_args[0]))
4326
def _translate_nobranch_error(err, find, get_path):
4327
if len(err.error_args) >= 1:
4328
extra = err.error_args[0].decode('utf-8')
4331
return errors.NotBranchError(path=find('bzrdir').root_transport.base,
4334
error_translators.register(b'nobranch', _translate_nobranch_error)
4335
error_translators.register(b'norepository',
4336
lambda err, find, get_path: errors.NoRepositoryPresent(
4338
error_translators.register(b'UnlockableTransport',
4339
lambda err, find, get_path: errors.UnlockableTransport(
4340
find('bzrdir').root_transport))
4341
error_translators.register(b'TokenMismatch',
4342
lambda err, find, get_path: errors.TokenMismatch(
4343
find('token'), '(remote token)'))
4344
error_translators.register(b'Diverged',
4345
lambda err, find, get_path: errors.DivergedBranches(
4346
find('branch'), find('other_branch')))
4347
error_translators.register(b'NotStacked',
4348
lambda err, find, get_path: errors.NotStacked(branch=find('branch')))
4350
def _translate_PermissionDenied(err, find, get_path):
4352
if len(err.error_args) >= 2:
4353
extra = err.error_args[1].decode('utf-8')
4356
return errors.PermissionDenied(path, extra=extra)
4358
error_translators.register(b'PermissionDenied', _translate_PermissionDenied)
4359
error_translators.register(b'ReadError',
4360
lambda err, find, get_path: errors.ReadError(get_path()))
4361
error_translators.register(b'NoSuchFile',
4362
lambda err, find, get_path: errors.NoSuchFile(get_path()))
4363
error_translators.register(b'TokenLockingNotSupported',
4364
lambda err, find, get_path: errors.TokenLockingNotSupported(
4365
find('repository')))
4366
error_translators.register(b'UnsuspendableWriteGroup',
4367
lambda err, find, get_path: errors.UnsuspendableWriteGroup(
4368
repository=find('repository')))
4369
error_translators.register(b'UnresumableWriteGroup',
4370
lambda err, find, get_path: errors.UnresumableWriteGroup(
4371
repository=find('repository'), write_groups=err.error_args[0],
4372
reason=err.error_args[1]))
4373
no_context_error_translators.register(b'GhostRevisionsHaveNoRevno',
4374
lambda err: errors.GhostRevisionsHaveNoRevno(*err.error_args))
4375
no_context_error_translators.register(b'IncompatibleRepositories',
4376
lambda err: errors.IncompatibleRepositories(
4377
err.error_args[0].decode('utf-8'), err.error_args[1].decode('utf-8'), err.error_args[2].decode('utf-8')))
4378
no_context_error_translators.register(b'LockContention',
4379
lambda err: errors.LockContention('(remote lock)'))
4380
no_context_error_translators.register(b'LockFailed',
4381
lambda err: errors.LockFailed(err.error_args[0].decode('utf-8'), err.error_args[1].decode('utf-8')))
4382
no_context_error_translators.register(b'TipChangeRejected',
4383
lambda err: errors.TipChangeRejected(err.error_args[0].decode('utf8')))
4384
no_context_error_translators.register(b'UnstackableBranchFormat',
4385
lambda err: branch.UnstackableBranchFormat(*err.error_args))
4386
no_context_error_translators.register(b'UnstackableRepositoryFormat',
4387
lambda err: errors.UnstackableRepositoryFormat(*err.error_args))
4388
no_context_error_translators.register(b'FileExists',
4389
lambda err: errors.FileExists(err.error_args[0].decode('utf-8')))
4390
no_context_error_translators.register(b'DirectoryNotEmpty',
4391
lambda err: errors.DirectoryNotEmpty(err.error_args[0].decode('utf-8')))
4392
no_context_error_translators.register(b'UnknownFormat',
4393
lambda err: errors.UnknownFormatError(
4394
err.error_args[0].decode('ascii'), err.error_args[0].decode('ascii')))
4395
no_context_error_translators.register(b'InvalidURL',
4396
lambda err: urlutils.InvalidURL(
4397
err.error_args[0].decode('utf-8'), err.error_args[1].decode('ascii')))
4399
def _translate_short_readv_error(err):
4400
args = err.error_args
4401
return errors.ShortReadvError(
4402
args[0].decode('utf-8'),
4403
int(args[1].decode('ascii')), int(args[2].decode('ascii')),
4404
int(args[3].decode('ascii')))
4406
no_context_error_translators.register(b'ShortReadvError',
4407
_translate_short_readv_error)
4409
def _translate_unicode_error(err):
4410
encoding = err.error_args[0].decode('ascii')
4411
val = err.error_args[1].decode('utf-8')
4412
start = int(err.error_args[2].decode('ascii'))
4413
end = int(err.error_args[3].decode('ascii'))
4414
reason = err.error_args[4].decode('utf-8')
2937
4415
if val.startswith('u:'):
2938
4416
val = val[2:].decode('utf-8')
2939
4417
elif val.startswith('s:'):