96
71
return self._client.call_with_body_bytes_expecting_body(
97
72
method, args, body_bytes)
98
except errors.ErrorFromSmartServer as err:
73
except errors.ErrorFromSmartServer, err:
99
74
self._translate_error(err, **err_context)
102
77
def response_tuple_to_repo_format(response):
103
78
"""Convert a response tuple describing a repository format to a format."""
104
79
format = RemoteRepositoryFormat()
105
format._rich_root_data = (response[0] == b'yes')
106
format._supports_tree_reference = (response[1] == b'yes')
107
format._supports_external_lookups = (response[2] == b'yes')
80
format._rich_root_data = (response[0] == 'yes')
81
format._supports_tree_reference = (response[1] == 'yes')
82
format._supports_external_lookups = (response[2] == 'yes')
108
83
format._network_name = response[3]
112
# Note that RemoteBzrDirProber lives in breezy.bzrdir so breezy.bzr.remote
113
# does not have to be imported unless a remote format is involved.
115
class RemoteBzrDirFormat(_mod_bzrdir.BzrDirMetaFormat1):
116
"""Format representing bzrdirs accessed via a smart server"""
118
supports_workingtrees = False
120
colocated_branches = False
123
_mod_bzrdir.BzrDirMetaFormat1.__init__(self)
124
# XXX: It's a bit ugly that the network name is here, because we'd
125
# like to believe that format objects are stateless or at least
126
# immutable, However, we do at least avoid mutating the name after
127
# it's returned. See <https://bugs.launchpad.net/bzr/+bug/504102>
128
self._network_name = None
131
return "%s(_network_name=%r)" % (self.__class__.__name__,
134
def get_format_description(self):
135
if self._network_name:
137
real_format = controldir.network_format_registry.get(
142
return 'Remote: ' + real_format.get_format_description()
143
return 'bzr remote bzrdir'
145
def get_format_string(self):
146
raise NotImplementedError(self.get_format_string)
148
def network_name(self):
149
if self._network_name:
150
return self._network_name
152
raise AssertionError("No network name set.")
154
def initialize_on_transport(self, transport):
156
# hand off the request to the smart server
157
client_medium = transport.get_smart_medium()
158
except errors.NoSmartMedium:
159
# TODO: lookup the local format from a server hint.
160
local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
161
return local_dir_format.initialize_on_transport(transport)
162
client = _SmartClient(client_medium)
163
path = client.remote_path_from_transport(transport)
165
response = client.call(b'BzrDirFormat.initialize', path)
166
except errors.ErrorFromSmartServer as err:
167
_translate_error(err, path=path)
168
if response[0] != b'ok':
169
raise errors.SmartProtocolError(
170
'unexpected response code %s' % (response,))
171
format = RemoteBzrDirFormat()
172
self._supply_sub_formats_to(format)
173
return RemoteBzrDir(transport, format)
175
def parse_NoneTrueFalse(self, arg):
182
raise AssertionError("invalid arg %r" % arg)
184
def _serialize_NoneTrueFalse(self, arg):
191
def _serialize_NoneString(self, arg):
194
def initialize_on_transport_ex(self, transport, use_existing_dir=False,
195
create_prefix=False, force_new_repo=False, stacked_on=None,
196
stack_on_pwd=None, repo_format_name=None, make_working_trees=None,
199
# hand off the request to the smart server
200
client_medium = transport.get_smart_medium()
201
except errors.NoSmartMedium:
204
# Decline to open it if the server doesn't support our required
205
# version (3) so that the VFS-based transport will do it.
206
if client_medium.should_probe():
208
server_version = client_medium.protocol_version()
209
if server_version != '2':
213
except errors.SmartProtocolError:
214
# Apparently there's no usable smart server there, even though
215
# the medium supports the smart protocol.
220
client = _SmartClient(client_medium)
221
path = client.remote_path_from_transport(transport)
222
if client_medium._is_remote_before((1, 16)):
225
# TODO: lookup the local format from a server hint.
226
local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
227
self._supply_sub_formats_to(local_dir_format)
228
return local_dir_format.initialize_on_transport_ex(transport,
229
use_existing_dir=use_existing_dir, create_prefix=create_prefix,
230
force_new_repo=force_new_repo, stacked_on=stacked_on,
231
stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
232
make_working_trees=make_working_trees, shared_repo=shared_repo,
234
return self._initialize_on_transport_ex_rpc(client, path, transport,
235
use_existing_dir, create_prefix, force_new_repo, stacked_on,
236
stack_on_pwd, repo_format_name, make_working_trees, shared_repo)
238
def _initialize_on_transport_ex_rpc(self, client, path, transport,
239
use_existing_dir, create_prefix, force_new_repo, stacked_on,
240
stack_on_pwd, repo_format_name, make_working_trees, shared_repo):
242
args.append(self._serialize_NoneTrueFalse(use_existing_dir))
243
args.append(self._serialize_NoneTrueFalse(create_prefix))
244
args.append(self._serialize_NoneTrueFalse(force_new_repo))
245
args.append(self._serialize_NoneString(stacked_on))
246
# stack_on_pwd is often/usually our transport
249
stack_on_pwd = transport.relpath(stack_on_pwd).encode('utf-8')
252
except errors.PathNotChild:
254
args.append(self._serialize_NoneString(stack_on_pwd))
255
args.append(self._serialize_NoneString(repo_format_name))
256
args.append(self._serialize_NoneTrueFalse(make_working_trees))
257
args.append(self._serialize_NoneTrueFalse(shared_repo))
258
request_network_name = self._network_name or \
259
_mod_bzrdir.BzrDirFormat.get_default_format().network_name()
261
response = client.call(b'BzrDirFormat.initialize_ex_1.16',
262
request_network_name, path, *args)
263
except errors.UnknownSmartMethod:
264
client._medium._remember_remote_is_before((1, 16))
265
local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
266
self._supply_sub_formats_to(local_dir_format)
267
return local_dir_format.initialize_on_transport_ex(transport,
268
use_existing_dir=use_existing_dir, create_prefix=create_prefix,
269
force_new_repo=force_new_repo, stacked_on=stacked_on,
270
stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
271
make_working_trees=make_working_trees, shared_repo=shared_repo,
273
except errors.ErrorFromSmartServer as err:
274
_translate_error(err, path=path.decode('utf-8'))
275
repo_path = response[0]
276
bzrdir_name = response[6]
277
require_stacking = response[7]
278
require_stacking = self.parse_NoneTrueFalse(require_stacking)
279
format = RemoteBzrDirFormat()
280
format._network_name = bzrdir_name
281
self._supply_sub_formats_to(format)
282
bzrdir = RemoteBzrDir(transport, format, _client=client)
284
repo_format = response_tuple_to_repo_format(response[1:])
285
if repo_path == b'.':
287
repo_path = repo_path.decode('utf-8')
289
repo_bzrdir_format = RemoteBzrDirFormat()
290
repo_bzrdir_format._network_name = response[5]
291
repo_bzr = RemoteBzrDir(transport.clone(repo_path),
295
final_stack = response[8] or None
297
final_stack = final_stack.decode('utf-8')
298
final_stack_pwd = response[9] or None
300
final_stack_pwd = urlutils.join(
301
transport.base, final_stack_pwd.decode('utf-8'))
302
remote_repo = RemoteRepository(repo_bzr, repo_format)
303
if len(response) > 10:
304
# Updated server verb that locks remotely.
305
repo_lock_token = response[10] or None
306
remote_repo.lock_write(repo_lock_token, _skip_rpc=True)
308
remote_repo.dont_leave_lock_in_place()
310
remote_repo.lock_write()
311
policy = _mod_bzrdir.UseExistingRepository(remote_repo,
312
final_stack, final_stack_pwd, require_stacking)
313
policy.acquire_repository()
317
bzrdir._format.set_branch_format(self.get_branch_format())
319
# The repo has already been created, but we need to make sure that
320
# we'll make a stackable branch.
321
bzrdir._format.require_stacking(_skip_repo=True)
322
return remote_repo, bzrdir, require_stacking, policy
324
def _open(self, transport):
325
return RemoteBzrDir(transport, self)
327
def __eq__(self, other):
328
if not isinstance(other, RemoteBzrDirFormat):
330
return self.get_format_description() == other.get_format_description()
332
def __return_repository_format(self):
333
# Always return a RemoteRepositoryFormat object, but if a specific bzr
334
# repository format has been asked for, tell the RemoteRepositoryFormat
335
# that it should use that for init() etc.
336
result = RemoteRepositoryFormat()
337
custom_format = getattr(self, '_repository_format', None)
339
if isinstance(custom_format, RemoteRepositoryFormat):
342
# We will use the custom format to create repositories over the
343
# wire; expose its details like rich_root_data for code to
345
result._custom_format = custom_format
348
def get_branch_format(self):
349
result = _mod_bzrdir.BzrDirMetaFormat1.get_branch_format(self)
350
if not isinstance(result, RemoteBranchFormat):
351
new_result = RemoteBranchFormat()
352
new_result._custom_format = result
354
self.set_branch_format(new_result)
358
repository_format = property(__return_repository_format,
359
_mod_bzrdir.BzrDirMetaFormat1._set_repository_format) # .im_func)
362
class RemoteControlStore(_mod_config.IniFileStore):
363
"""Control store which attempts to use HPSS calls to retrieve control store.
365
Note that this is specific to bzr-based formats.
368
def __init__(self, bzrdir):
369
super(RemoteControlStore, self).__init__()
370
self.controldir = bzrdir
371
self._real_store = None
373
def lock_write(self, token=None):
375
return self._real_store.lock_write(token)
379
return self._real_store.unlock()
382
with self.lock_write():
383
# We need to be able to override the undecorated implementation
384
self.save_without_locking()
386
def save_without_locking(self):
387
super(RemoteControlStore, self).save()
389
def _ensure_real(self):
390
self.controldir._ensure_real()
391
if self._real_store is None:
392
self._real_store = _mod_config.ControlStore(self.controldir)
394
def external_url(self):
395
return urlutils.join(self.branch.user_url, 'control.conf')
397
def _load_content(self):
398
medium = self.controldir._client._medium
399
path = self.controldir._path_for_remote_call(self.controldir._client)
401
response, handler = self.controldir._call_expecting_body(
402
b'BzrDir.get_config_file', path)
403
except errors.UnknownSmartMethod:
405
return self._real_store._load_content()
406
if len(response) and response[0] != b'ok':
407
raise errors.UnexpectedSmartServerResponse(response)
408
return handler.read_body_bytes()
410
def _save_content(self, content):
411
# FIXME JRV 2011-11-22: Ideally this should use a
412
# HPSS call too, but at the moment it is not possible
413
# to write lock control directories.
415
return self._real_store._save_content(content)
418
class RemoteBzrDir(_mod_bzrdir.BzrDir, _RpcHelper):
87
# Note: RemoteBzrDirFormat is in bzrdir.py
89
class RemoteBzrDir(BzrDir, _RpcHelper):
419
90
"""Control directory on a remote server, accessed via bzr:// or similar."""
421
def __init__(self, transport, format, _client=None, _force_probe=False):
92
def __init__(self, transport, format, _client=None):
422
93
"""Construct a RemoteBzrDir.
424
95
:param _client: Private parameter for testing. Disables probing and the
425
96
use of a real bzrdir.
427
_mod_bzrdir.BzrDir.__init__(self, transport, format)
98
BzrDir.__init__(self, transport, format)
428
99
# this object holds a delegated bzrdir that uses file-level operations
429
100
# to talk to the other side
430
101
self._real_bzrdir = None
431
self._has_working_tree = None
432
102
# 1-shot cache for the call pattern 'create_branch; open_branch' - see
433
103
# create_branch for details.
434
104
self._next_open_branch_result = None
555
146
medium = self._client._medium
556
147
if medium._is_remote_before((1, 13)):
557
148
return self._vfs_cloning_metadir(require_stacking=require_stacking)
558
verb = b'BzrDir.cloning_metadir'
149
verb = 'BzrDir.cloning_metadir'
559
150
if require_stacking:
563
154
path = self._path_for_remote_call(self._client)
565
156
response = self._call(verb, path, stacking)
566
157
except errors.UnknownSmartMethod:
567
158
medium._remember_remote_is_before((1, 13))
568
159
return self._vfs_cloning_metadir(require_stacking=require_stacking)
569
except errors.UnknownErrorFromSmartServer as err:
570
if err.error_tuple != (b'BranchReference',):
160
except errors.UnknownErrorFromSmartServer, err:
161
if err.error_tuple != ('BranchReference',):
572
163
# We need to resolve the branch reference to determine the
573
164
# cloning_metadir. This causes unnecessary RPCs to open the
574
165
# referenced branch (and bzrdir, etc) but only when the caller
575
166
# didn't already resolve the branch reference.
576
167
referenced_branch = self.open_branch()
577
return referenced_branch.controldir.cloning_metadir()
168
return referenced_branch.bzrdir.cloning_metadir()
578
169
if len(response) != 3:
579
170
raise errors.UnexpectedSmartServerResponse(response)
580
171
control_name, repo_name, branch_info = response
581
172
if len(branch_info) != 2:
582
173
raise errors.UnexpectedSmartServerResponse(response)
583
174
branch_ref, branch_name = branch_info
585
format = controldir.network_format_registry.get(control_name)
587
raise errors.UnknownFormatError(
588
kind='control', format=control_name)
175
format = bzrdir.network_format_registry.get(control_name)
592
format.repository_format = _mod_repository.network_format_registry.get(
595
raise errors.UnknownFormatError(kind='repository',
597
if branch_ref == b'ref':
177
format.repository_format = repository.network_format_registry.get(
179
if branch_ref == 'ref':
598
180
# XXX: we need possible_transports here to avoid reopening the
599
181
# connection to the referenced location
600
ref_bzrdir = _mod_bzrdir.BzrDir.open(branch_name)
182
ref_bzrdir = BzrDir.open(branch_name)
601
183
branch_format = ref_bzrdir.cloning_metadir().get_branch_format()
602
184
format.set_branch_format(branch_format)
603
elif branch_ref == b'branch':
185
elif branch_ref == 'branch':
606
branch_format = branch.network_format_registry.get(
609
raise errors.UnknownFormatError(kind='branch',
611
format.set_branch_format(branch_format)
187
format.set_branch_format(
188
branch.network_format_registry.get(branch_name))
613
190
raise errors.UnexpectedSmartServerResponse(response)
663
221
self._next_open_branch_result = result
666
def destroy_branch(self, name=None):
224
def destroy_branch(self):
667
225
"""See BzrDir.destroy_branch"""
669
name = self._get_selected_branch()
671
raise errors.NoColocatedBranchSupport(self)
672
path = self._path_for_remote_call(self._client)
678
response = self._call(b'BzrDir.destroy_branch', path, *args)
679
except errors.UnknownSmartMethod:
681
self._real_bzrdir.destroy_branch(name=name)
682
self._next_open_branch_result = None
227
self._real_bzrdir.destroy_branch()
684
228
self._next_open_branch_result = None
685
if response[0] != b'ok':
686
raise SmartProtocolError(
687
'unexpected response code %s' % (response,))
689
def create_workingtree(self, revision_id=None, from_branch=None,
690
accelerator_tree=None, hardlink=False):
230
def create_workingtree(self, revision_id=None, from_branch=None):
691
231
raise errors.NotLocalUrl(self.transport.base)
693
def find_branch_format(self, name=None):
233
def find_branch_format(self):
694
234
"""Find the branch 'format' for this bzrdir.
696
236
This might be a synthetic object for e.g. RemoteBranch and SVN.
698
b = self.open_branch(name=name)
238
b = self.open_branch()
701
def branch_names(self):
702
path = self._path_for_remote_call(self._client)
704
response, handler = self._call_expecting_body(
705
b'BzrDir.get_branches', path)
706
except errors.UnknownSmartMethod:
708
return self._real_bzrdir.branch_names()
709
if response[0] != b"success":
710
raise errors.UnexpectedSmartServerResponse(response)
711
body = bencode.bdecode(handler.read_body_bytes())
713
for name, value in body.items():
714
name = name.decode('utf-8')
718
def get_branches(self, possible_transports=None, ignore_fallbacks=False):
719
path = self._path_for_remote_call(self._client)
721
response, handler = self._call_expecting_body(
722
b'BzrDir.get_branches', path)
723
except errors.UnknownSmartMethod:
725
return self._real_bzrdir.get_branches()
726
if response[0] != b"success":
727
raise errors.UnexpectedSmartServerResponse(response)
728
body = bencode.bdecode(handler.read_body_bytes())
730
for name, value in body.items():
731
name = name.decode('utf-8')
732
ret[name] = self._open_branch(
733
name, value[0].decode('ascii'), value[1],
734
possible_transports=possible_transports,
735
ignore_fallbacks=ignore_fallbacks)
738
def set_branch_reference(self, target_branch, name=None):
739
"""See BzrDir.set_branch_reference()."""
741
name = self._get_selected_branch()
743
raise errors.NoColocatedBranchSupport(self)
745
return self._real_bzrdir.set_branch_reference(target_branch, name=name)
747
def get_branch_reference(self, name=None):
241
def get_branch_reference(self):
748
242
"""See BzrDir.get_branch_reference()."""
750
name = self._get_selected_branch()
752
raise errors.NoColocatedBranchSupport(self)
753
243
response = self._get_branch_reference()
754
244
if response[0] == 'ref':
755
return response[1].decode('utf-8')
759
249
def _get_branch_reference(self):
760
"""Get branch reference information
762
:return: Tuple with (kind, location_or_format)
763
if kind == 'ref', then location_or_format contains a location
764
otherwise, it contains a format name
766
250
path = self._path_for_remote_call(self._client)
767
251
medium = self._client._medium
769
(b'BzrDir.open_branchV3', (2, 1)),
770
(b'BzrDir.open_branchV2', (1, 13)),
771
(b'BzrDir.open_branch', None),
773
for verb, required_version in candidate_calls:
774
if required_version and medium._is_remote_before(required_version):
252
if not medium._is_remote_before((1, 13)):
777
response = self._call(verb, path)
254
response = self._call('BzrDir.open_branchV2', path)
255
if response[0] not in ('ref', 'branch'):
256
raise errors.UnexpectedSmartServerResponse(response)
778
258
except errors.UnknownSmartMethod:
779
if required_version is None:
781
medium._remember_remote_is_before(required_version)
784
if verb == b'BzrDir.open_branch':
785
if response[0] != b'ok':
786
raise errors.UnexpectedSmartServerResponse(response)
787
if response[1] != b'':
788
return ('ref', response[1])
790
return ('branch', b'')
791
if response[0] not in (b'ref', b'branch'):
259
medium._remember_remote_is_before((1, 13))
260
response = self._call('BzrDir.open_branch', path)
261
if response[0] != 'ok':
792
262
raise errors.UnexpectedSmartServerResponse(response)
793
return (response[0].decode('ascii'), response[1])
263
if response[1] != '':
264
return ('ref', response[1])
266
return ('branch', '')
795
def _get_tree_branch(self, name=None):
268
def _get_tree_branch(self):
796
269
"""See BzrDir._get_tree_branch()."""
797
return None, self.open_branch(name=name)
270
return None, self.open_branch()
799
def _open_branch(self, name, kind, location_or_format,
800
ignore_fallbacks=False, possible_transports=None):
272
def open_branch(self, _unsupported=False, ignore_fallbacks=False):
274
raise NotImplementedError('unsupported flag support not implemented yet.')
275
if self._next_open_branch_result is not None:
276
# See create_branch for details.
277
result = self._next_open_branch_result
278
self._next_open_branch_result = None
280
response = self._get_branch_reference()
281
if response[0] == 'ref':
802
282
# a branch reference, use the existing BranchReference logic.
803
283
format = BranchReferenceFormat()
804
ref_loc = urlutils.join(self.user_url, location_or_format.decode('utf-8'))
805
return format.open(self, name=name, _found=True,
807
ignore_fallbacks=ignore_fallbacks,
808
possible_transports=possible_transports)
809
branch_format_name = location_or_format
284
return format.open(self, _found=True, location=response[1],
285
ignore_fallbacks=ignore_fallbacks)
286
branch_format_name = response[1]
810
287
if not branch_format_name:
811
288
branch_format_name = None
812
289
format = RemoteBranchFormat(network_name=branch_format_name)
813
290
return RemoteBranch(self, self.find_repository(), format=format,
814
setup_stacking=not ignore_fallbacks, name=name,
815
possible_transports=possible_transports)
817
def open_branch(self, name=None, unsupported=False,
818
ignore_fallbacks=False, possible_transports=None):
820
name = self._get_selected_branch()
822
raise errors.NoColocatedBranchSupport(self)
824
raise NotImplementedError(
825
'unsupported flag support not implemented yet.')
826
if self._next_open_branch_result is not None:
827
# See create_branch for details.
828
result = self._next_open_branch_result
829
self._next_open_branch_result = None
831
response = self._get_branch_reference()
832
return self._open_branch(name, response[0], response[1],
833
possible_transports=possible_transports,
834
ignore_fallbacks=ignore_fallbacks)
291
setup_stacking=not ignore_fallbacks)
836
293
def _open_repo_v1(self, path):
837
verb = b'BzrDir.find_repository'
294
verb = 'BzrDir.find_repository'
838
295
response = self._call(verb, path)
839
if response[0] != b'ok':
296
if response[0] != 'ok':
840
297
raise errors.UnexpectedSmartServerResponse(response)
841
298
# servers that only support the v1 method don't support external
842
299
# references either.
843
300
self._ensure_real()
844
301
repo = self._real_bzrdir.open_repository()
845
response = response + (b'no', repo._format.network_name())
302
response = response + ('no', repo._format.network_name())
846
303
return response, repo
848
305
def _open_repo_v2(self, path):
849
verb = b'BzrDir.find_repositoryV2'
306
verb = 'BzrDir.find_repositoryV2'
850
307
response = self._call(verb, path)
851
if response[0] != b'ok':
308
if response[0] != 'ok':
852
309
raise errors.UnexpectedSmartServerResponse(response)
853
310
self._ensure_real()
854
311
repo = self._real_bzrdir.open_repository()
945
382
"""Upgrading of remote bzrdirs is not supported yet."""
948
def needs_format_conversion(self, format):
385
def needs_format_conversion(self, format=None):
949
386
"""Upgrading of remote bzrdirs is not supported yet."""
388
symbol_versioning.warn(symbol_versioning.deprecated_in((1, 13, 0))
389
% 'needs_format_conversion(format=None)')
392
def clone(self, url, revision_id=None, force_new_repo=False,
393
preserve_stacking=False):
395
return self._real_bzrdir.clone(url, revision_id=revision_id,
396
force_new_repo=force_new_repo, preserve_stacking=preserve_stacking)
952
398
def _get_config(self):
953
399
return RemoteBzrDirConfig(self)
955
def _get_config_store(self):
956
return RemoteControlStore(self)
959
class RemoteInventoryTree(InventoryRevisionTree):
961
def __init__(self, repository, inv, revision_id):
962
super(RemoteInventoryTree, self).__init__(repository, inv, revision_id)
964
def archive(self, format, name, root=None, subdir=None, force_mtime=None):
965
ret = self._repository._revision_archive(
966
self.get_revision_id(), format, name, root, subdir,
967
force_mtime=force_mtime)
969
return super(RemoteInventoryTree, self).archive(
970
format, name, root, subdir, force_mtime=force_mtime)
973
def annotate_iter(self, path,
974
default_revision=_mod_revision.CURRENT_REVISION):
975
"""Return an iterator of revision_id, line tuples.
977
For working trees (and mutable trees in general), the special
978
revision_id 'current:' will be used for lines that are new in this
979
tree, e.g. uncommitted changes.
980
:param default_revision: For lines that don't match a basis, mark them
981
with this revision id. Not all implementations will make use of
984
ret = self._repository._annotate_file_revision(
985
self.get_revision_id(), path, file_id=None,
986
default_revision=default_revision)
988
return super(RemoteInventoryTree, self).annotate_iter(
989
path, default_revision=default_revision)
993
class RemoteRepositoryFormat(vf_repository.VersionedFileRepositoryFormat):
402
class RemoteRepositoryFormat(repository.RepositoryFormat):
994
403
"""Format for repositories accessed over a _SmartClient.
996
405
Instances of this repository are represented by RemoteRepository
1083
470
self._custom_format.supports_tree_reference
1084
471
return self._supports_tree_reference
1087
def revision_graph_can_have_wrong_parents(self):
1088
if self._revision_graph_can_have_wrong_parents is None:
1090
self._revision_graph_can_have_wrong_parents = \
1091
self._custom_format.revision_graph_can_have_wrong_parents
1092
return self._revision_graph_can_have_wrong_parents
1094
def _vfs_initialize(self, a_controldir, shared):
473
def _vfs_initialize(self, a_bzrdir, shared):
1095
474
"""Helper for common code in initialize."""
1096
475
if self._custom_format:
1097
476
# Custom format requested
1098
result = self._custom_format.initialize(
1099
a_controldir, shared=shared)
477
result = self._custom_format.initialize(a_bzrdir, shared=shared)
1100
478
elif self._creating_bzrdir is not None:
1101
479
# Use the format that the repository we were created to back
1103
481
prior_repo = self._creating_bzrdir.open_repository()
1104
482
prior_repo._ensure_real()
1105
483
result = prior_repo._real_repository._format.initialize(
1106
a_controldir, shared=shared)
484
a_bzrdir, shared=shared)
1108
486
# assume that a_bzr is a RemoteBzrDir but the smart server didn't
1109
487
# support remote initialization.
1110
488
# We delegate to a real object at this point (as RemoteBzrDir
1111
489
# delegate to the repository format which would lead to infinite
1112
# recursion if we just called a_controldir.create_repository.
1113
a_controldir._ensure_real()
1114
result = a_controldir._real_bzrdir.create_repository(shared=shared)
490
# recursion if we just called a_bzrdir.create_repository.
491
a_bzrdir._ensure_real()
492
result = a_bzrdir._real_bzrdir.create_repository(shared=shared)
1115
493
if not isinstance(result, RemoteRepository):
1116
return self.open(a_controldir)
494
return self.open(a_bzrdir)
1120
def initialize(self, a_controldir, shared=False):
498
def initialize(self, a_bzrdir, shared=False):
1121
499
# Being asked to create on a non RemoteBzrDir:
1122
if not isinstance(a_controldir, RemoteBzrDir):
1123
return self._vfs_initialize(a_controldir, shared)
1124
medium = a_controldir._client._medium
500
if not isinstance(a_bzrdir, RemoteBzrDir):
501
return self._vfs_initialize(a_bzrdir, shared)
502
medium = a_bzrdir._client._medium
1125
503
if medium._is_remote_before((1, 13)):
1126
return self._vfs_initialize(a_controldir, shared)
504
return self._vfs_initialize(a_bzrdir, shared)
1127
505
# Creating on a remote bzr dir.
1128
506
# 1) get the network name to use.
1129
507
if self._custom_format:
1540
837
# TODO: Move to RepositoryBase and unify with the regular Repository
1541
838
# one; unfortunately the tests rely on slightly different behaviour at
1542
839
# present -- mbp 20090710
1543
return (self.__class__ is other.__class__
1544
and self.controldir.transport.base == other.controldir.transport.base)
840
return (self.__class__ is other.__class__ and
841
self.bzrdir.transport.base == other.bzrdir.transport.base)
1546
843
def get_graph(self, other_repository=None):
1547
844
"""Return the graph for this repository format"""
1548
845
parents_provider = self._make_parents_provider(other_repository)
1549
846
return graph.Graph(parents_provider)
1551
def get_known_graph_ancestry(self, revision_ids):
1552
"""Return the known graph for a set of revision ids and their ancestors.
1554
with self.lock_read():
1555
revision_graph = dict(((key, value) for key, value in
1556
self.get_graph().iter_ancestry(revision_ids) if value is not None))
1557
revision_graph = _mod_repository._strip_NULL_ghosts(revision_graph)
1558
return graph.KnownGraph(revision_graph)
1560
848
def gather_stats(self, revid=None, committers=None):
1561
849
"""See Repository.gather_stats()."""
1562
path = self.controldir._path_for_remote_call(self._client)
850
path = self.bzrdir._path_for_remote_call(self._client)
1563
851
# revid can be None to indicate no revisions, not just NULL_REVISION
1564
if revid is None or _mod_revision.is_null(revid):
852
if revid is None or revision.is_null(revid):
1567
855
fmt_revid = revid
1568
856
if committers is None or not committers:
1569
fmt_committers = b'no'
857
fmt_committers = 'no'
1571
fmt_committers = b'yes'
859
fmt_committers = 'yes'
1572
860
response_tuple, response_handler = self._call_expecting_body(
1573
b'Repository.gather_stats', path, fmt_revid, fmt_committers)
1574
if response_tuple[0] != b'ok':
861
'Repository.gather_stats', path, fmt_revid, fmt_committers)
862
if response_tuple[0] != 'ok':
1575
863
raise errors.UnexpectedSmartServerResponse(response_tuple)
1577
865
body = response_handler.read_body_bytes()
1579
for line in body.split(b'\n'):
867
for line in body.split('\n'):
1582
key, val_text = line.split(b':')
1583
key = key.decode('ascii')
870
key, val_text = line.split(':')
1584
871
if key in ('revisions', 'size', 'committers'):
1585
872
result[key] = int(val_text)
1586
873
elif key in ('firstrev', 'latestrev'):
1587
values = val_text.split(b' ')[1:]
1588
result[key] = (float(values[0]), int(values[1]))
874
values = val_text.split(' ')[1:]
875
result[key] = (float(values[0]), long(values[1]))
1869
1106
raise errors.UnexpectedSmartServerResponse(response)
1871
1108
def sprout(self, to_bzrdir, revision_id=None):
1872
"""Create a descendent repository for new development.
1874
Unlike clone, this does not copy the settings of the repository.
1876
with self.lock_read():
1877
dest_repo = self._create_sprouting_repo(to_bzrdir, shared=False)
1878
dest_repo.fetch(self, revision_id=revision_id)
1881
def _create_sprouting_repo(self, a_controldir, shared):
1882
if not isinstance(a_controldir._format, self.controldir._format.__class__):
1883
# use target default format.
1884
dest_repo = a_controldir.create_repository()
1886
# Most control formats need the repository to be specifically
1887
# created, but on some old all-in-one formats it's not needed
1889
dest_repo = self._format.initialize(
1890
a_controldir, shared=shared)
1891
except errors.UninitializableFormat:
1892
dest_repo = a_controldir.open_repository()
1109
# TODO: Option to control what format is created?
1111
dest_repo = self._real_repository._format.initialize(to_bzrdir,
1113
dest_repo.fetch(self, revision_id=revision_id)
1893
1114
return dest_repo
1895
# These methods are just thin shims to the VFS object for now.
1116
### These methods are just thin shims to the VFS object for now.
1897
1118
def revision_tree(self, revision_id):
1898
with self.lock_read():
1899
revision_id = _mod_revision.ensure_null(revision_id)
1900
if revision_id == _mod_revision.NULL_REVISION:
1901
return InventoryRevisionTree(self,
1902
Inventory(root_id=None), _mod_revision.NULL_REVISION)
1904
return list(self.revision_trees([revision_id]))[0]
1120
return self._real_repository.revision_tree(revision_id)
1906
1122
def get_serializer_format(self):
1907
path = self.controldir._path_for_remote_call(self._client)
1909
response = self._call(b'VersionedFileRepository.get_serializer_format',
1911
except errors.UnknownSmartMethod:
1913
return self._real_repository.get_serializer_format()
1914
if response[0] != b'ok':
1915
raise errors.UnexpectedSmartServerResponse(response)
1124
return self._real_repository.get_serializer_format()
1918
1126
def get_commit_builder(self, branch, parents, config, timestamp=None,
1919
1127
timezone=None, committer=None, revprops=None,
1920
revision_id=None, lossy=False):
1921
"""Obtain a CommitBuilder for this repository.
1923
:param branch: Branch to commit to.
1924
:param parents: Revision ids of the parents of the new revision.
1925
:param config: Configuration to use.
1926
:param timestamp: Optional timestamp recorded for commit.
1927
:param timezone: Optional timezone for timestamp.
1928
:param committer: Optional committer to set for commit.
1929
:param revprops: Optional dictionary of revision properties.
1930
:param revision_id: Optional revision id.
1931
:param lossy: Whether to discard data that can not be natively
1932
represented, when pushing to a foreign VCS
1934
if self._fallback_repositories and not self._format.supports_chks:
1935
raise errors.BzrError("Cannot commit directly to a stacked branch"
1936
" in pre-2a formats. See "
1937
"https://bugs.launchpad.net/bzr/+bug/375013 for details.")
1938
commit_builder_kls = vf_repository.VersionedFileCommitBuilder
1939
result = commit_builder_kls(self, parents, config,
1940
timestamp, timezone, committer, revprops, revision_id,
1942
self.start_write_group()
1129
# FIXME: It ought to be possible to call this without immediately
1130
# triggering _ensure_real. For now it's the easiest thing to do.
1132
real_repo = self._real_repository
1133
builder = real_repo.get_commit_builder(branch, parents,
1134
config, timestamp=timestamp, timezone=timezone,
1135
committer=committer, revprops=revprops, revision_id=revision_id)
1945
1138
def add_fallback_repository(self, repository):
1946
1139
"""Add a repository to use for looking up data not held locally.
1965
1156
# _real_branch had its get_stacked_on_url method called), then the
1966
1157
# repository to be added may already be in the _real_repositories list.
1967
1158
if self._real_repository is not None:
1968
fallback_locations = [repo.user_url for repo in
1969
self._real_repository._fallback_repositories]
1970
if repository.user_url not in fallback_locations:
1159
fallback_locations = [repo.bzrdir.root_transport.base for repo in
1160
self._real_repository._fallback_repositories]
1161
if repository.bzrdir.root_transport.base not in fallback_locations:
1971
1162
self._real_repository.add_fallback_repository(repository)
1973
def _check_fallback_repository(self, repository):
1974
"""Check that this repository can fallback to repository safely.
1976
Raise an error if not.
1978
:param repository: A repository to fallback to.
1980
return _mod_repository.InterRepository._assert_same_model(
1983
1164
def add_inventory(self, revid, inv, parents):
1984
1165
self._ensure_real()
1985
1166
return self._real_repository.add_inventory(revid, inv, parents)
1987
1168
def add_inventory_by_delta(self, basis_revision_id, delta, new_revision_id,
1988
parents, basis_inv=None, propagate_caches=False):
1989
1170
self._ensure_real()
1990
1171
return self._real_repository.add_inventory_by_delta(basis_revision_id,
1991
delta, new_revision_id, parents, basis_inv=basis_inv,
1992
propagate_caches=propagate_caches)
1994
def add_revision(self, revision_id, rev, inv=None):
1995
_mod_revision.check_not_reserved_id(revision_id)
1996
key = (revision_id,)
1997
# check inventory present
1998
if not self.inventories.get_parent_map([key]):
2000
raise errors.WeaveRevisionNotPresent(revision_id,
2003
# yes, this is not suitable for adding with ghosts.
2004
rev.inventory_sha1 = self.add_inventory(revision_id, inv,
2007
rev.inventory_sha1 = self.inventories.get_sha1s([key])[key]
2008
self._add_revision(rev)
2010
def _add_revision(self, rev):
2011
if self._real_repository is not None:
2012
return self._real_repository._add_revision(rev)
2013
lines = self._serializer.write_revision_to_lines(rev)
2014
key = (rev.revision_id,)
2015
parents = tuple((parent,) for parent in rev.parent_ids)
2016
self._write_group_tokens, missing_keys = self._get_sink().insert_stream(
2017
[('revisions', [ChunkedContentFactory(key, parents, None, lines, chunks_are_lines=True)])],
2018
self._format, self._write_group_tokens)
1172
delta, new_revision_id, parents)
1174
def add_revision(self, rev_id, rev, inv=None, config=None):
1176
return self._real_repository.add_revision(
1177
rev_id, rev, inv=inv, config=config)
2020
1180
def get_inventory(self, revision_id):
2021
with self.lock_read():
2022
return list(self.iter_inventories([revision_id]))[0]
2024
def _iter_inventories_rpc(self, revision_ids, ordering):
2025
if ordering is None:
2026
ordering = 'unordered'
2027
path = self.controldir._path_for_remote_call(self._client)
2028
body = b"\n".join(revision_ids)
2029
response_tuple, response_handler = (
2030
self._call_with_body_bytes_expecting_body(
2031
b"VersionedFileRepository.get_inventories",
2032
(path, ordering.encode('ascii')), body))
2033
if response_tuple[0] != b"ok":
2034
raise errors.UnexpectedSmartServerResponse(response_tuple)
2035
deserializer = inventory_delta.InventoryDeltaDeserializer()
2036
byte_stream = response_handler.read_streamed_body()
2037
decoded = smart_repo._byte_stream_to_stream(byte_stream)
2039
# no results whatsoever
2041
src_format, stream = decoded
2042
if src_format.network_name() != self._format.network_name():
2043
raise AssertionError(
2044
"Mismatched RemoteRepository and stream src %r, %r" % (
2045
src_format.network_name(), self._format.network_name()))
2046
# ignore the src format, it's not really relevant
2047
prev_inv = Inventory(root_id=None,
2048
revision_id=_mod_revision.NULL_REVISION)
2049
# there should be just one substream, with inventory deltas
2051
substream_kind, substream = next(stream)
2052
except StopIteration:
2054
if substream_kind != "inventory-deltas":
2055
raise AssertionError(
2056
"Unexpected stream %r received" % substream_kind)
2057
for record in substream:
2058
(parent_id, new_id, versioned_root, tree_references, invdelta) = (
2059
deserializer.parse_text_bytes(record.get_bytes_as("lines")))
2060
if parent_id != prev_inv.revision_id:
2061
raise AssertionError("invalid base %r != %r" % (parent_id,
2062
prev_inv.revision_id))
2063
inv = prev_inv.create_by_apply_delta(invdelta, new_id)
2064
yield inv, inv.revision_id
2067
def _iter_inventories_vfs(self, revision_ids, ordering=None):
2068
1181
self._ensure_real()
2069
return self._real_repository._iter_inventories(revision_ids, ordering)
1182
return self._real_repository.get_inventory(revision_id)
2071
1184
def iter_inventories(self, revision_ids, ordering=None):
2072
"""Get many inventories by revision_ids.
2074
This will buffer some or all of the texts used in constructing the
2075
inventories in memory, but will only parse a single inventory at a
2078
:param revision_ids: The expected revision ids of the inventories.
2079
:param ordering: optional ordering, e.g. 'topological'. If not
2080
specified, the order of revision_ids will be preserved (by
2081
buffering if necessary).
2082
:return: An iterator of inventories.
2084
if ((None in revision_ids) or
2085
(_mod_revision.NULL_REVISION in revision_ids)):
2086
raise ValueError('cannot get null revision inventory')
2087
for inv, revid in self._iter_inventories(revision_ids, ordering):
2089
raise errors.NoSuchRevision(self, revid)
2092
def _iter_inventories(self, revision_ids, ordering=None):
2093
if len(revision_ids) == 0:
2095
missing = set(revision_ids)
2096
if ordering is None:
2097
order_as_requested = True
2099
order = list(revision_ids)
2101
next_revid = order.pop()
2103
order_as_requested = False
2104
if ordering != 'unordered' and self._fallback_repositories:
2105
raise ValueError('unsupported ordering %r' % ordering)
2106
iter_inv_fns = [self._iter_inventories_rpc] + [
2107
fallback._iter_inventories for fallback in
2108
self._fallback_repositories]
2110
for iter_inv in iter_inv_fns:
2111
request = [revid for revid in revision_ids if revid in missing]
2112
for inv, revid in iter_inv(request, ordering):
2115
missing.remove(inv.revision_id)
2116
if ordering != 'unordered':
2120
if order_as_requested:
2121
# Yield as many results as we can while preserving order.
2122
while next_revid in invs:
2123
inv = invs.pop(next_revid)
2124
yield inv, inv.revision_id
2126
next_revid = order.pop()
2128
# We still want to fully consume the stream, just
2129
# in case it is not actually finished at this point
2132
except errors.UnknownSmartMethod:
2133
for inv, revid in self._iter_inventories_vfs(revision_ids, ordering):
2137
if order_as_requested:
2138
if next_revid is not None:
2139
yield None, next_revid
2142
yield invs.get(revid), revid
2145
yield None, missing.pop()
1186
return self._real_repository.iter_inventories(revision_ids, ordering)
2147
1189
def get_revision(self, revision_id):
2148
with self.lock_read():
2149
return self.get_revisions([revision_id])[0]
1191
return self._real_repository.get_revision(revision_id)
2151
1193
def get_transaction(self):
2152
1194
self._ensure_real()
2153
1195
return self._real_repository.get_transaction()
2155
def clone(self, a_controldir, revision_id=None):
2156
with self.lock_read():
2157
dest_repo = self._create_sprouting_repo(
2158
a_controldir, shared=self.is_shared())
2159
self.copy_content_into(dest_repo, revision_id)
1198
def clone(self, a_bzrdir, revision_id=None):
1200
return self._real_repository.clone(a_bzrdir, revision_id=revision_id)
2162
1202
def make_working_trees(self):
2163
1203
"""See Repository.make_working_trees"""
2164
path = self.controldir._path_for_remote_call(self._client)
2166
response = self._call(b'Repository.make_working_trees', path)
2167
except errors.UnknownSmartMethod:
2169
return self._real_repository.make_working_trees()
2170
if response[0] not in (b'yes', b'no'):
2171
raise SmartProtocolError(
2172
'unexpected response code %s' % (response,))
2173
return response[0] == b'yes'
1205
return self._real_repository.make_working_trees()
2175
1207
def refresh_data(self):
2176
"""Re-read any data needed to synchronise with disk.
1208
"""Re-read any data needed to to synchronise with disk.
2178
1210
This method is intended to be called after another repository instance
2179
1211
(such as one used by a smart server) has inserted data into the
2180
repository. On all repositories this will work outside of write groups.
2181
Some repository formats (pack and newer for breezy native formats)
2182
support refresh_data inside write groups. If called inside a write
2183
group on a repository that does not support refreshing in a write group
2184
IsInWriteGroupError will be raised.
1212
repository. It may not be called during a write group, but may be
1213
called at any other time.
1215
if self.is_in_write_group():
1216
raise errors.InternalBzrError(
1217
"May not refresh_data while in a write group.")
2186
1218
if self._real_repository is not None:
2187
1219
self._real_repository.refresh_data()
2188
# Refresh the parents cache for this object
2189
self._unstacked_provider.disable_cache()
2190
self._unstacked_provider.enable_cache()
2192
1221
def revision_ids_to_search_result(self, result_set):
2193
1222
"""Convert a set of revision ids to a graph SearchResult."""
2194
1223
result_parents = set()
2195
for parents in self.get_graph().get_parent_map(result_set).values():
1224
for parents in self.get_graph().get_parent_map(
1225
result_set).itervalues():
2196
1226
result_parents.update(parents)
2197
1227
included_keys = result_set.intersection(result_parents)
2198
1228
start_keys = result_set.difference(included_keys)
2199
1229
exclude_keys = result_parents.difference(result_set)
2200
result = vf_search.SearchResult(start_keys, exclude_keys,
2201
len(result_set), result_set)
1230
result = graph.SearchResult(start_keys, exclude_keys,
1231
len(result_set), result_set)
2204
def search_missing_revision_ids(self, other,
2205
find_ghosts=True, revision_ids=None, if_present_ids=None,
1235
def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
2207
1236
"""Return the revision ids that other has that this does not.
2209
1238
These are returned in topological order.
2211
1240
revision_id: only return revision ids included by revision_id.
2213
with self.lock_read():
2214
inter_repo = _mod_repository.InterRepository.get(other, self)
2215
return inter_repo.search_missing_revision_ids(
2216
find_ghosts=find_ghosts, revision_ids=revision_ids,
2217
if_present_ids=if_present_ids, limit=limit)
1242
return repository.InterRepository.get(
1243
other, self).search_missing_revision_ids(revision_id, find_ghosts)
2219
def fetch(self, source, revision_id=None, find_ghosts=False,
2220
fetch_spec=None, lossy=False):
1245
def fetch(self, source, revision_id=None, pb=None, find_ghosts=False,
2221
1247
# No base implementation to use as RemoteRepository is not a subclass
2222
1248
# of Repository; so this is a copy of Repository.fetch().
2223
1249
if fetch_spec is not None and revision_id is not None:
2261
1287
return self._real_repository._get_versioned_file_checker(
2262
1288
revisions, revision_versions_cache)
2264
def _iter_files_bytes_rpc(self, desired_files, absent):
2265
path = self.controldir._path_for_remote_call(self._client)
2268
for (file_id, revid, identifier) in desired_files:
2269
lines.append(b''.join([
2273
identifiers.append(identifier)
2274
(response_tuple, response_handler) = (
2275
self._call_with_body_bytes_expecting_body(
2276
b"Repository.iter_files_bytes", (path, ), b"\n".join(lines)))
2277
if response_tuple != (b'ok', ):
2278
response_handler.cancel_read_body()
2279
raise errors.UnexpectedSmartServerResponse(response_tuple)
2280
byte_stream = response_handler.read_streamed_body()
2282
def decompress_stream(start, byte_stream, unused):
2283
decompressor = zlib.decompressobj()
2284
yield decompressor.decompress(start)
2285
while decompressor.unused_data == b"":
2287
data = next(byte_stream)
2288
except StopIteration:
2290
yield decompressor.decompress(data)
2291
yield decompressor.flush()
2292
unused.append(decompressor.unused_data)
2295
while b"\n" not in unused:
2297
unused += next(byte_stream)
2298
except StopIteration:
2300
header, rest = unused.split(b"\n", 1)
2301
args = header.split(b"\0")
2302
if args[0] == b"absent":
2303
absent[identifiers[int(args[3])]] = (args[1], args[2])
2306
elif args[0] == b"ok":
2309
raise errors.UnexpectedSmartServerResponse(args)
2311
yield (identifiers[idx],
2312
decompress_stream(rest, byte_stream, unused_chunks))
2313
unused = b"".join(unused_chunks)
2315
1290
def iter_files_bytes(self, desired_files):
2316
1291
"""See Repository.iter_file_bytes.
2320
for (identifier, bytes_iterator) in self._iter_files_bytes_rpc(
2321
desired_files, absent):
2322
yield identifier, bytes_iterator
2323
for fallback in self._fallback_repositories:
2326
desired_files = [(key[0], key[1], identifier)
2327
for identifier, key in absent.items()]
2328
for (identifier, bytes_iterator) in fallback.iter_files_bytes(desired_files):
2329
del absent[identifier]
2330
yield identifier, bytes_iterator
2332
# There may be more missing items, but raise an exception
2334
missing_identifier = next(iter(absent))
2335
missing_key = absent[missing_identifier]
2336
raise errors.RevisionNotPresent(revision_id=missing_key[1],
2337
file_id=missing_key[0])
2338
except errors.UnknownSmartMethod:
2340
for (identifier, bytes_iterator) in (
2341
self._real_repository.iter_files_bytes(desired_files)):
2342
yield identifier, bytes_iterator
2344
def get_cached_parent_map(self, revision_ids):
2345
"""See breezy.CachingParentsProvider.get_cached_parent_map"""
2346
return self._unstacked_provider.get_cached_parent_map(revision_ids)
1294
return self._real_repository.iter_files_bytes(desired_files)
2348
1296
def get_parent_map(self, revision_ids):
2349
"""See breezy.Graph.get_parent_map()."""
1297
"""See bzrlib.Graph.get_parent_map()."""
2350
1298
return self._make_parents_provider().get_parent_map(revision_ids)
2352
1300
def _get_parent_map_rpc(self, keys):
2469
1428
revision_graph[d[0]] = (NULL_REVISION,)
2470
1429
return revision_graph
2472
1432
def get_signature_text(self, revision_id):
2473
with self.lock_read():
2474
path = self.controldir._path_for_remote_call(self._client)
2476
response_tuple, response_handler = self._call_expecting_body(
2477
b'Repository.get_revision_signature_text', path, revision_id)
2478
except errors.UnknownSmartMethod:
2480
return self._real_repository.get_signature_text(revision_id)
2481
except errors.NoSuchRevision as err:
2482
for fallback in self._fallback_repositories:
2484
return fallback.get_signature_text(revision_id)
2485
except errors.NoSuchRevision:
2489
if response_tuple[0] != b'ok':
2490
raise errors.UnexpectedSmartServerResponse(response_tuple)
2491
return response_handler.read_body_bytes()
2493
def _get_inventory_xml(self, revision_id):
2494
with self.lock_read():
2495
# This call is used by older working tree formats,
2496
# which stored a serialized basis inventory.
2498
return self._real_repository._get_inventory_xml(revision_id)
1434
return self._real_repository.get_signature_text(revision_id)
1437
def get_inventory_xml(self, revision_id):
1439
return self._real_repository.get_inventory_xml(revision_id)
1441
def deserialise_inventory(self, revision_id, xml):
1443
return self._real_repository.deserialise_inventory(revision_id, xml)
2500
1445
def reconcile(self, other=None, thorough=False):
2501
from ..reconcile import ReconcileResult
2502
with self.lock_write():
2503
path = self.controldir._path_for_remote_call(self._client)
2505
response, handler = self._call_expecting_body(
2506
b'Repository.reconcile', path, self._lock_token)
2507
except (errors.UnknownSmartMethod, errors.TokenLockingNotSupported):
2509
return self._real_repository.reconcile(other=other, thorough=thorough)
2510
if response != (b'ok', ):
2511
raise errors.UnexpectedSmartServerResponse(response)
2512
body = handler.read_body_bytes()
2513
result = ReconcileResult()
2514
result.garbage_inventories = None
2515
result.inconsistent_parents = None
2516
result.aborted = None
2517
for line in body.split(b'\n'):
2520
key, val_text = line.split(b':')
2521
if key == b"garbage_inventories":
2522
result.garbage_inventories = int(val_text)
2523
elif key == b"inconsistent_parents":
2524
result.inconsistent_parents = int(val_text)
2526
mutter("unknown reconcile key %r" % key)
1447
return self._real_repository.reconcile(other=other, thorough=thorough)
2529
1449
def all_revision_ids(self):
2530
path = self.controldir._path_for_remote_call(self._client)
2532
response_tuple, response_handler = self._call_expecting_body(
2533
b"Repository.all_revision_ids", path)
2534
except errors.UnknownSmartMethod:
2536
return self._real_repository.all_revision_ids()
2537
if response_tuple != (b"ok", ):
2538
raise errors.UnexpectedSmartServerResponse(response_tuple)
2539
revids = set(response_handler.read_body_bytes().splitlines())
2540
for fallback in self._fallback_repositories:
2541
revids.update(set(fallback.all_revision_ids()))
2544
def _filtered_revision_trees(self, revision_ids, file_ids):
2545
"""Return Tree for a revision on this branch with only some files.
2547
:param revision_ids: a sequence of revision-ids;
2548
a revision-id may not be None or b'null:'
2549
:param file_ids: if not None, the result is filtered
2550
so that only those file-ids, their parents and their
2551
children are included.
2553
inventories = self.iter_inventories(revision_ids)
2554
for inv in inventories:
2555
# Should we introduce a FilteredRevisionTree class rather
2556
# than pre-filter the inventory here?
2557
filtered_inv = inv.filter(file_ids)
2558
yield InventoryRevisionTree(self, filtered_inv, filtered_inv.revision_id)
2560
def get_revision_delta(self, revision_id):
2561
with self.lock_read():
2562
r = self.get_revision(revision_id)
2563
return list(self.get_revision_deltas([r]))[0]
1451
return self._real_repository.all_revision_ids()
1454
def get_deltas_for_revisions(self, revisions, specific_fileids=None):
1456
return self._real_repository.get_deltas_for_revisions(revisions,
1457
specific_fileids=specific_fileids)
1460
def get_revision_delta(self, revision_id, specific_fileids=None):
1462
return self._real_repository.get_revision_delta(revision_id,
1463
specific_fileids=specific_fileids)
2565
1466
def revision_trees(self, revision_ids):
2566
with self.lock_read():
2567
inventories = self.iter_inventories(revision_ids)
2568
for inv in inventories:
2569
yield RemoteInventoryTree(self, inv, inv.revision_id)
1468
return self._real_repository.revision_trees(revision_ids)
2571
1471
def get_revision_reconcile(self, revision_id):
2572
with self.lock_read():
2574
return self._real_repository.get_revision_reconcile(revision_id)
1473
return self._real_repository.get_revision_reconcile(revision_id)
2576
1476
def check(self, revision_ids=None, callback_refs=None, check_repo=True):
2577
with self.lock_read():
2579
return self._real_repository.check(revision_ids=revision_ids,
2580
callback_refs=callback_refs, check_repo=check_repo)
1478
return self._real_repository.check(revision_ids=revision_ids,
1479
callback_refs=callback_refs, check_repo=check_repo)
2582
1481
def copy_content_into(self, destination, revision_id=None):
2583
"""Make a complete copy of the content in self into destination.
2585
This is a destructive operation! Do not use it on existing
2588
interrepo = _mod_repository.InterRepository.get(self, destination)
2589
return interrepo.copy_content(revision_id)
1483
return self._real_repository.copy_content_into(
1484
destination, revision_id=revision_id)
2591
1486
def _copy_repository_tarball(self, to_bzrdir, revision_id=None):
2592
1487
# get a tarball of the remote repository, and copy from that into the
1489
from bzrlib import osutils
2595
1491
# TODO: Maybe a progress bar while streaming the tarball?
2596
note(gettext("Copying repository content as tarball..."))
1492
note("Copying repository content as tarball...")
2597
1493
tar_file = self._get_tarball('bz2')
2598
1494
if tar_file is None:
2600
1496
destination = to_bzrdir.create_repository()
2602
1498
tar = tarfile.open('repository', fileobj=tar_file,
2604
1500
tmpdir = osutils.mkdtemp()
2606
tar.extractall(tmpdir)
2607
tmp_bzrdir = _mod_bzrdir.BzrDir.open(tmpdir)
1502
_extract_tar(tar, tmpdir)
1503
tmp_bzrdir = BzrDir.open(tmpdir)
2608
1504
tmp_repo = tmp_bzrdir.open_repository()
2609
1505
tmp_repo.copy_content_into(destination, revision_id)
2698
1583
self._ensure_real()
2699
1584
return self._real_repository.texts
2701
def _iter_revisions_rpc(self, revision_ids):
2702
body = b"\n".join(revision_ids)
2703
path = self.controldir._path_for_remote_call(self._client)
2704
response_tuple, response_handler = (
2705
self._call_with_body_bytes_expecting_body(
2706
b"Repository.iter_revisions", (path, ), body))
2707
if response_tuple[0] != b"ok":
2708
raise errors.UnexpectedSmartServerResponse(response_tuple)
2709
serializer_format = response_tuple[1].decode('ascii')
2710
serializer = serializer_format_registry.get(serializer_format)
2711
byte_stream = response_handler.read_streamed_body()
2712
decompressor = zlib.decompressobj()
2714
for bytes in byte_stream:
2715
chunks.append(decompressor.decompress(bytes))
2716
if decompressor.unused_data != b"":
2717
chunks.append(decompressor.flush())
2718
yield serializer.read_revision_from_string(b"".join(chunks))
2719
unused = decompressor.unused_data
2720
decompressor = zlib.decompressobj()
2721
chunks = [decompressor.decompress(unused)]
2722
chunks.append(decompressor.flush())
2723
text = b"".join(chunks)
2725
yield serializer.read_revision_from_string(b"".join(chunks))
2727
def iter_revisions(self, revision_ids):
2728
for rev_id in revision_ids:
2729
if not rev_id or not isinstance(rev_id, bytes):
2730
raise errors.InvalidRevisionId(
2731
revision_id=rev_id, branch=self)
2732
with self.lock_read():
2734
missing = set(revision_ids)
2735
for rev in self._iter_revisions_rpc(revision_ids):
2736
missing.remove(rev.revision_id)
2737
yield (rev.revision_id, rev)
2738
for fallback in self._fallback_repositories:
2741
for (revid, rev) in fallback.iter_revisions(missing):
2744
missing.remove(revid)
2745
for revid in missing:
2747
except errors.UnknownSmartMethod:
2749
for entry in self._real_repository.iter_revisions(revision_ids):
1587
def get_revisions(self, revision_ids):
1589
return self._real_repository.get_revisions(revision_ids)
2752
1591
def supports_rich_root(self):
2753
1592
return self._format.rich_root_data
1594
def iter_reverse_revision_history(self, revision_id):
1596
return self._real_repository.iter_reverse_revision_history(revision_id)
2756
1599
def _serializer(self):
2757
1600
return self._format._serializer
2759
1602
def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
2760
with self.lock_write():
2761
signature = gpg_strategy.sign(plaintext, gpg.MODE_CLEAR)
2762
self.add_signature_text(revision_id, signature)
1604
return self._real_repository.store_revision_signature(
1605
gpg_strategy, plaintext, revision_id)
2764
1607
def add_signature_text(self, revision_id, signature):
2765
if self._real_repository:
2766
# If there is a real repository the write group will
2767
# be in the real repository as well, so use that:
2769
return self._real_repository.add_signature_text(
2770
revision_id, signature)
2771
path = self.controldir._path_for_remote_call(self._client)
2772
response, handler = self._call_with_body_bytes_expecting_body(
2773
b'Repository.add_signature_text', (path, self._lock_token,
2775
tuple([token.encode('utf-8')
2776
for token in self._write_group_tokens]),
2778
handler.cancel_read_body()
2780
if response[0] != b'ok':
2781
raise errors.UnexpectedSmartServerResponse(response)
2782
self._write_group_tokens = [token.decode(
2783
'utf-8') for token in response[1:]]
1609
return self._real_repository.add_signature_text(revision_id, signature)
2785
1611
def has_signature_for_revision_id(self, revision_id):
2786
path = self.controldir._path_for_remote_call(self._client)
2788
response = self._call(b'Repository.has_signature_for_revision_id',
2790
except errors.UnknownSmartMethod:
2792
return self._real_repository.has_signature_for_revision_id(
2794
if response[0] not in (b'yes', b'no'):
2795
raise SmartProtocolError(
2796
'unexpected response code %s' % (response,))
2797
if response[0] == b'yes':
2799
for fallback in self._fallback_repositories:
2800
if fallback.has_signature_for_revision_id(revision_id):
2804
def verify_revision_signature(self, revision_id, gpg_strategy):
2805
with self.lock_read():
2806
if not self.has_signature_for_revision_id(revision_id):
2807
return gpg.SIGNATURE_NOT_SIGNED, None
2808
signature = self.get_signature_text(revision_id)
2810
testament = _mod_testament.Testament.from_revision(
2813
(status, key, signed_plaintext) = gpg_strategy.verify(signature)
2814
if testament.as_short_text() != signed_plaintext:
2815
return gpg.SIGNATURE_NOT_VALID, None
2816
return (status, key)
1613
return self._real_repository.has_signature_for_revision_id(revision_id)
2818
1615
def item_keys_introduced_by(self, revision_ids, _files_pb=None):
2819
1616
self._ensure_real()
2820
1617
return self._real_repository.item_keys_introduced_by(revision_ids,
2821
_files_pb=_files_pb)
1618
_files_pb=_files_pb)
1620
def revision_graph_can_have_wrong_parents(self):
1621
# The answer depends on the remote repo format.
1623
return self._real_repository.revision_graph_can_have_wrong_parents()
2823
1625
def _find_inconsistent_revision_parents(self, revisions_iterator=None):
2824
1626
self._ensure_real()
2842
1645
:param recipe: A search recipe (start, stop, count).
2843
1646
:return: Serialised bytes.
2845
start_keys = b' '.join(recipe[1])
2846
stop_keys = b' '.join(recipe[2])
2847
count = str(recipe[3]).encode('ascii')
2848
return b'\n'.join((start_keys, stop_keys, count))
1648
start_keys = ' '.join(recipe[1])
1649
stop_keys = ' '.join(recipe[2])
1650
count = str(recipe[3])
1651
return '\n'.join((start_keys, stop_keys, count))
2850
1653
def _serialise_search_result(self, search_result):
2851
parts = search_result.get_network_struct()
2852
return b'\n'.join(parts)
1654
if isinstance(search_result, graph.PendingAncestryResult):
1655
parts = ['ancestry-of']
1656
parts.extend(search_result.heads)
1658
recipe = search_result.get_recipe()
1659
parts = [recipe[0], self._serialise_search_recipe(recipe)]
1660
return '\n'.join(parts)
2854
1662
def autopack(self):
2855
path = self.controldir._path_for_remote_call(self._client)
1663
path = self.bzrdir._path_for_remote_call(self._client)
2857
response = self._call(b'PackRepository.autopack', path)
1665
response = self._call('PackRepository.autopack', path)
2858
1666
except errors.UnknownSmartMethod:
2859
1667
self._ensure_real()
2860
1668
self._real_repository._pack_collection.autopack()
2862
1670
self.refresh_data()
2863
if response[0] != b'ok':
2864
raise errors.UnexpectedSmartServerResponse(response)
2866
def _revision_archive(self, revision_id, format, name, root, subdir,
2868
path = self.controldir._path_for_remote_call(self._client)
2869
format = format or ''
2871
subdir = subdir or ''
2872
force_mtime = int(force_mtime) if force_mtime is not None else None
2874
response, protocol = self._call_expecting_body(
2875
b'Repository.revision_archive', path,
2877
format.encode('ascii'),
2878
os.path.basename(name).encode('utf-8'),
2879
root.encode('utf-8'),
2880
subdir.encode('utf-8'),
2882
except errors.UnknownSmartMethod:
2884
if response[0] == b'ok':
2885
return iter([protocol.read_body_bytes()])
2886
raise errors.UnexpectedSmartServerResponse(response)
2888
def _annotate_file_revision(self, revid, tree_path, file_id, default_revision):
2889
path = self.controldir._path_for_remote_call(self._client)
2890
tree_path = tree_path.encode('utf-8')
2891
file_id = file_id or b''
2892
default_revision = default_revision or b''
2894
response, handler = self._call_expecting_body(
2895
b'Repository.annotate_file_revision', path,
2896
revid, tree_path, file_id, default_revision)
2897
except errors.UnknownSmartMethod:
2899
if response[0] != b'ok':
2900
raise errors.UnexpectedSmartServerResponse(response)
2901
return map(tuple, bencode.bdecode(handler.read_body_bytes()))
2904
class RemoteStreamSink(vf_repository.StreamSink):
1671
if response[0] != 'ok':
1672
raise errors.UnexpectedSmartServerResponse(response)
1675
class RemoteStreamSink(repository.StreamSink):
2906
1677
def _insert_real(self, stream, src_format, resume_tokens):
2907
1678
self.target_repo._ensure_real()
3227
1940
def __init__(self, network_name=None):
3228
1941
super(RemoteBranchFormat, self).__init__()
3229
self._matchingcontroldir = RemoteBzrDirFormat()
3230
self._matchingcontroldir.set_branch_format(self)
1942
self._matchingbzrdir = RemoteBzrDirFormat()
1943
self._matchingbzrdir.set_branch_format(self)
3231
1944
self._custom_format = None
3232
1945
self._network_name = network_name
3234
1947
def __eq__(self, other):
3235
return (isinstance(other, RemoteBranchFormat)
3236
and self.__dict__ == other.__dict__)
1948
return (isinstance(other, RemoteBranchFormat) and
1949
self.__dict__ == other.__dict__)
3238
1951
def _ensure_real(self):
3239
1952
if self._custom_format is None:
3241
self._custom_format = branch.network_format_registry.get(
3244
raise errors.UnknownFormatError(kind='branch',
3245
format=self._network_name)
1953
self._custom_format = branch.network_format_registry.get(
3247
1956
def get_format_description(self):
3249
return 'Remote: ' + self._custom_format.get_format_description()
1957
return 'Remote BZR Branch'
3251
1959
def network_name(self):
3252
1960
return self._network_name
3254
def open(self, a_controldir, name=None, ignore_fallbacks=False):
3255
return a_controldir.open_branch(name=name,
3256
ignore_fallbacks=ignore_fallbacks)
1962
def open(self, a_bzrdir, ignore_fallbacks=False):
1963
return a_bzrdir.open_branch(ignore_fallbacks=ignore_fallbacks)
3258
def _vfs_initialize(self, a_controldir, name, append_revisions_only,
1965
def _vfs_initialize(self, a_bzrdir):
3260
1966
# Initialisation when using a local bzrdir object, or a non-vfs init
3261
1967
# method is not available on the server.
3262
1968
# self._custom_format is always set - the start of initialize ensures
3264
if isinstance(a_controldir, RemoteBzrDir):
3265
a_controldir._ensure_real()
3266
result = self._custom_format.initialize(a_controldir._real_bzrdir,
3267
name=name, append_revisions_only=append_revisions_only,
3268
repository=repository)
1970
if isinstance(a_bzrdir, RemoteBzrDir):
1971
a_bzrdir._ensure_real()
1972
result = self._custom_format.initialize(a_bzrdir._real_bzrdir)
3270
1974
# We assume the bzrdir is parameterised; it may not be.
3271
result = self._custom_format.initialize(a_controldir, name=name,
3272
append_revisions_only=append_revisions_only,
3273
repository=repository)
3274
if (isinstance(a_controldir, RemoteBzrDir)
3275
and not isinstance(result, RemoteBranch)):
3276
result = RemoteBranch(a_controldir, a_controldir.find_repository(), result,
1975
result = self._custom_format.initialize(a_bzrdir)
1976
if (isinstance(a_bzrdir, RemoteBzrDir) and
1977
not isinstance(result, RemoteBranch)):
1978
result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result)
3280
def initialize(self, a_controldir, name=None, repository=None,
3281
append_revisions_only=None):
3283
name = a_controldir._get_selected_branch()
1981
def initialize(self, a_bzrdir):
3284
1982
# 1) get the network name to use.
3285
1983
if self._custom_format:
3286
1984
network_name = self._custom_format.network_name()
3288
# Select the current breezy default and ask for that.
3289
reference_bzrdir_format = controldir.format_registry.get(
1986
# Select the current bzrlib default and ask for that.
1987
reference_bzrdir_format = bzrdir.format_registry.get('default')()
3291
1988
reference_format = reference_bzrdir_format.get_branch_format()
3292
1989
self._custom_format = reference_format
3293
1990
network_name = reference_format.network_name()
3294
1991
# Being asked to create on a non RemoteBzrDir:
3295
if not isinstance(a_controldir, RemoteBzrDir):
3296
return self._vfs_initialize(a_controldir, name=name,
3297
append_revisions_only=append_revisions_only,
3298
repository=repository)
3299
medium = a_controldir._client._medium
1992
if not isinstance(a_bzrdir, RemoteBzrDir):
1993
return self._vfs_initialize(a_bzrdir)
1994
medium = a_bzrdir._client._medium
3300
1995
if medium._is_remote_before((1, 13)):
3301
return self._vfs_initialize(a_controldir, name=name,
3302
append_revisions_only=append_revisions_only,
3303
repository=repository)
1996
return self._vfs_initialize(a_bzrdir)
3304
1997
# Creating on a remote bzr dir.
3305
1998
# 2) try direct creation via RPC
3306
path = a_controldir._path_for_remote_call(a_controldir._client)
3308
# XXX JRV20100304: Support creating colocated branches
3309
raise errors.NoColocatedBranchSupport(self)
3310
verb = b'BzrDir.create_branch'
1999
path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
2000
verb = 'BzrDir.create_branch'
3312
response = a_controldir._call(verb, path, network_name)
2002
response = a_bzrdir._call(verb, path, network_name)
3313
2003
except errors.UnknownSmartMethod:
3314
2004
# Fallback - use vfs methods
3315
2005
medium._remember_remote_is_before((1, 13))
3316
return self._vfs_initialize(a_controldir, name=name,
3317
append_revisions_only=append_revisions_only,
3318
repository=repository)
3319
if response[0] != b'ok':
2006
return self._vfs_initialize(a_bzrdir)
2007
if response[0] != 'ok':
3320
2008
raise errors.UnexpectedSmartServerResponse(response)
3321
2009
# Turn the response into a RemoteRepository object.
3322
2010
format = RemoteBranchFormat(network_name=response[1])
3323
2011
repo_format = response_tuple_to_repo_format(response[3:])
3324
repo_path = response[2].decode('utf-8')
3325
if repository is not None:
3326
remote_repo_url = urlutils.join(a_controldir.user_url, repo_path)
3327
url_diff = urlutils.relative_url(repository.user_url,
3330
raise AssertionError(
3331
'repository.user_url %r does not match URL from server '
3332
'response (%r + %r)'
3333
% (repository.user_url, a_controldir.user_url, repo_path))
3334
remote_repo = repository
2012
if response[2] == '':
2013
repo_bzrdir = a_bzrdir
3337
repo_bzrdir = a_controldir
3339
repo_bzrdir = RemoteBzrDir(
3340
a_controldir.root_transport.clone(
3341
repo_path), a_controldir._format,
3342
a_controldir._client)
3343
remote_repo = RemoteRepository(repo_bzrdir, repo_format)
3344
remote_branch = RemoteBranch(a_controldir, remote_repo,
3345
format=format, setup_stacking=False, name=name)
3346
if append_revisions_only:
3347
remote_branch.set_append_revisions_only(append_revisions_only)
2015
repo_bzrdir = RemoteBzrDir(
2016
a_bzrdir.root_transport.clone(response[2]), a_bzrdir._format,
2018
remote_repo = RemoteRepository(repo_bzrdir, repo_format)
2019
remote_branch = RemoteBranch(a_bzrdir, remote_repo,
2020
format=format, setup_stacking=False)
3348
2021
# XXX: We know this is a new branch, so it must have revno 0, revid
3349
2022
# NULL_REVISION. Creating the branch locked would make this be unable
3350
2023
# to be wrong; here its simply very unlikely to be wrong. RBC 20090225
3369
2042
self._ensure_real()
3370
2043
return self._custom_format.supports_set_append_revisions_only()
3373
def supports_reference_locations(self):
3375
return self._custom_format.supports_reference_locations
3377
def stores_revno(self):
3380
def _use_default_local_heads_to_fetch(self):
3381
# If the branch format is a metadir format *and* its heads_to_fetch
3382
# implementation is not overridden vs the base class, we can use the
3383
# base class logic rather than use the heads_to_fetch RPC. This is
3384
# usually cheaper in terms of net round trips, as the last-revision and
3385
# tags info fetched is cached and would be fetched anyway.
3387
if isinstance(self._custom_format, bzrbranch.BranchFormatMetadir):
3388
branch_class = self._custom_format._branch_class()
3389
heads_to_fetch_impl = branch_class.heads_to_fetch
3390
if heads_to_fetch_impl is branch.Branch.heads_to_fetch:
3395
class RemoteBranchStore(_mod_config.IniFileStore):
3396
"""Branch store which attempts to use HPSS calls to retrieve branch store.
3398
Note that this is specific to bzr-based formats.
3401
def __init__(self, branch):
3402
super(RemoteBranchStore, self).__init__()
3403
self.branch = branch
3405
self._real_store = None
3407
def external_url(self):
3408
return urlutils.join(self.branch.user_url, 'branch.conf')
3410
def _load_content(self):
3411
path = self.branch._remote_path()
3413
response, handler = self.branch._call_expecting_body(
3414
b'Branch.get_config_file', path)
3415
except errors.UnknownSmartMethod:
3417
return self._real_store._load_content()
3418
if len(response) and response[0] != b'ok':
3419
raise errors.UnexpectedSmartServerResponse(response)
3420
return handler.read_body_bytes()
3422
def _save_content(self, content):
3423
path = self.branch._remote_path()
3425
response, handler = self.branch._call_with_body_bytes_expecting_body(
3426
b'Branch.put_config_file', (path,
3427
self.branch._lock_token, self.branch._repo_lock_token),
3429
except errors.UnknownSmartMethod:
3431
return self._real_store._save_content(content)
3432
handler.cancel_read_body()
3433
if response != (b'ok', ):
3434
raise errors.UnexpectedSmartServerResponse(response)
3436
def _ensure_real(self):
3437
self.branch._ensure_real()
3438
if self._real_store is None:
3439
self._real_store = _mod_config.BranchStore(self.branch)
3442
class RemoteBranch(branch.Branch, _RpcHelper, lock._RelockDebugMixin):
2046
class RemoteBranch(branch.Branch, _RpcHelper):
3443
2047
"""Branch stored on a server accessed by HPSS RPC.
3445
2049
At the moment most operations are mapped down to simple file operations.
3448
2052
def __init__(self, remote_bzrdir, remote_repository, real_branch=None,
3449
_client=None, format=None, setup_stacking=True, name=None,
3450
possible_transports=None):
2053
_client=None, format=None, setup_stacking=True):
3451
2054
"""Create a RemoteBranch instance.
3453
2056
:param real_branch: An optional local implementation of the branch
3706
2264
return self._real_branch._set_tags_bytes(bytes)
3708
2266
def _set_tags_bytes(self, bytes):
3709
if self.is_locked():
3710
self._tags_bytes = bytes
3711
2267
medium = self._client._medium
3712
2268
if medium._is_remote_before((1, 18)):
3713
2269
self._vfs_set_tags_bytes(bytes)
3717
2272
self._remote_path(), self._lock_token, self._repo_lock_token)
3718
2273
response = self._call_with_body_bytes(
3719
b'Branch.set_tags_bytes', args, bytes)
2274
'Branch.set_tags_bytes', args, bytes)
3720
2275
except errors.UnknownSmartMethod:
3721
2276
medium._remember_remote_is_before((1, 18))
3722
2277
self._vfs_set_tags_bytes(bytes)
3724
2279
def lock_read(self):
3725
"""Lock the branch for read operations.
3727
:return: A breezy.lock.LogicalLockResult.
3729
2280
self.repository.lock_read()
3730
2281
if not self._lock_mode:
3731
self._note_lock('r')
3732
2282
self._lock_mode = 'r'
3733
2283
self._lock_count = 1
3734
2284
if self._real_branch is not None:
3735
2285
self._real_branch.lock_read()
3737
2287
self._lock_count += 1
3738
return lock.LogicalLockResult(self.unlock)
3740
2289
def _remote_lock_write(self, token):
3741
2290
if token is None:
3742
branch_token = repo_token = b''
2291
branch_token = repo_token = ''
3744
2293
branch_token = token
3745
repo_token = self.repository.lock_write().repository_token
2294
repo_token = self.repository.lock_write()
3746
2295
self.repository.unlock()
3747
2296
err_context = {'token': token}
3749
response = self._call(
3750
b'Branch.lock_write', self._remote_path(), branch_token,
3751
repo_token or b'', **err_context)
3752
except errors.LockContention as e:
3753
# The LockContention from the server doesn't have any
3754
# information about the lock_url. We re-raise LockContention
3755
# with valid lock_url.
3756
raise errors.LockContention('(remote lock)',
3757
self.repository.base.split('.bzr/')[0])
3758
if response[0] != b'ok':
2297
response = self._call(
2298
'Branch.lock_write', self._remote_path(), branch_token,
2299
repo_token or '', **err_context)
2300
if response[0] != 'ok':
3759
2301
raise errors.UnexpectedSmartServerResponse(response)
3760
2302
ok, branch_token, repo_token = response
3761
2303
return branch_token, repo_token
3763
2305
def lock_write(self, token=None):
3764
2306
if not self._lock_mode:
3765
self._note_lock('w')
3766
2307
# Lock the branch and repo in one remote call.
3767
2308
remote_tokens = self._remote_lock_write(token)
3768
2309
self._lock_token, self._repo_lock_token = remote_tokens
3769
2310
if not self._lock_token:
3770
raise SmartProtocolError(
3771
'Remote server did not return a token!')
2311
raise SmartProtocolError('Remote server did not return a token!')
3772
2312
# Tell the self.repository object that it is locked.
3773
2313
self.repository.lock_write(
3774
2314
self._repo_lock_token, _skip_rpc=True)
3992
2530
self._ensure_real()
3993
2531
return self._real_branch._set_parent_location(url)
3995
2534
def pull(self, source, overwrite=False, stop_revision=None,
3997
with self.lock_write():
3998
self._clear_cached_state_of_remote_branch_only()
4000
return self._real_branch.pull(
4001
source, overwrite=overwrite, stop_revision=stop_revision,
4002
_override_hook_target=self, **kwargs)
4004
def push(self, target, overwrite=False, stop_revision=None, lossy=False, tag_selector=None):
4005
with self.lock_read():
4007
return self._real_branch.push(
4008
target, overwrite=overwrite, stop_revision=stop_revision, lossy=lossy,
4009
_override_hook_source_branch=self, tag_selector=tag_selector)
4011
def peek_lock_mode(self):
4012
return self._lock_mode
2536
self._clear_cached_state_of_remote_branch_only()
2538
return self._real_branch.pull(
2539
source, overwrite=overwrite, stop_revision=stop_revision,
2540
_override_hook_target=self, **kwargs)
2543
def push(self, target, overwrite=False, stop_revision=None):
2545
return self._real_branch.push(
2546
target, overwrite=overwrite, stop_revision=stop_revision,
2547
_override_hook_source_branch=self)
4014
2549
def is_locked(self):
4015
2550
return self._lock_count >= 1
4017
def revision_id_to_dotted_revno(self, revision_id):
4018
"""Given a revision id, return its dotted revno.
4020
:return: a tuple like (1,) or (400,1,3).
4022
with self.lock_read():
4024
response = self._call(b'Branch.revision_id_to_revno',
4025
self._remote_path(), revision_id)
4026
except errors.UnknownSmartMethod:
4028
return self._real_branch.revision_id_to_dotted_revno(revision_id)
4029
except errors.UnknownErrorFromSmartServer as e:
4030
# Deal with older versions of bzr/brz that didn't explicitly
4031
# wrap GhostRevisionsHaveNoRevno.
4032
if e.error_tuple[1] == b'GhostRevisionsHaveNoRevno':
4033
(revid, ghost_revid) = re.findall(b"{([^}]+)}", e.error_tuple[2])
4034
raise errors.GhostRevisionsHaveNoRevno(
4037
if response[0] == b'ok':
4038
return tuple([int(x) for x in response[1:]])
4040
raise errors.UnexpectedSmartServerResponse(response)
4042
2553
def revision_id_to_revno(self, revision_id):
4043
"""Given a revision id on the branch mainline, return its revno.
4047
with self.lock_read():
4049
response = self._call(b'Branch.revision_id_to_revno',
4050
self._remote_path(), revision_id)
4051
except errors.UnknownSmartMethod:
4053
return self._real_branch.revision_id_to_revno(revision_id)
4054
if response[0] == b'ok':
4055
if len(response) == 2:
4056
return int(response[1])
4057
raise NoSuchRevision(self, revision_id)
4059
raise errors.UnexpectedSmartServerResponse(response)
2555
return self._real_branch.revision_id_to_revno(revision_id)
4061
2558
def set_last_revision_info(self, revno, revision_id):
4062
with self.lock_write():
4063
# XXX: These should be returned by the set_last_revision_info verb
4064
old_revno, old_revid = self.last_revision_info()
4065
self._run_pre_change_branch_tip_hooks(revno, revision_id)
4066
if not revision_id or not isinstance(revision_id, bytes):
4067
raise errors.InvalidRevisionId(
4068
revision_id=revision_id, branch=self)
4070
response = self._call(b'Branch.set_last_revision_info',
4071
self._remote_path(), self._lock_token, self._repo_lock_token,
4072
str(revno).encode('ascii'), revision_id)
4073
except errors.UnknownSmartMethod:
4075
self._clear_cached_state_of_remote_branch_only()
4076
self._real_branch.set_last_revision_info(revno, revision_id)
4077
self._last_revision_info_cache = revno, revision_id
4079
if response == (b'ok',):
4080
self._clear_cached_state()
4081
self._last_revision_info_cache = revno, revision_id
4082
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
4083
# Update the _real_branch's cache too.
4084
if self._real_branch is not None:
4085
cache = self._last_revision_info_cache
4086
self._real_branch._last_revision_info_cache = cache
4088
raise errors.UnexpectedSmartServerResponse(response)
2559
# XXX: These should be returned by the set_last_revision_info verb
2560
old_revno, old_revid = self.last_revision_info()
2561
self._run_pre_change_branch_tip_hooks(revno, revision_id)
2562
revision_id = ensure_null(revision_id)
2564
response = self._call('Branch.set_last_revision_info',
2565
self._remote_path(), self._lock_token, self._repo_lock_token,
2566
str(revno), revision_id)
2567
except errors.UnknownSmartMethod:
2569
self._clear_cached_state_of_remote_branch_only()
2570
self._real_branch.set_last_revision_info(revno, revision_id)
2571
self._last_revision_info_cache = revno, revision_id
2573
if response == ('ok',):
2574
self._clear_cached_state()
2575
self._last_revision_info_cache = revno, revision_id
2576
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2577
# Update the _real_branch's cache too.
2578
if self._real_branch is not None:
2579
cache = self._last_revision_info_cache
2580
self._real_branch._last_revision_info_cache = cache
2582
raise errors.UnexpectedSmartServerResponse(response)
4090
2585
def generate_revision_history(self, revision_id, last_rev=None,
4091
2586
other_branch=None):
4092
with self.lock_write():
4093
medium = self._client._medium
4094
if not medium._is_remote_before((1, 6)):
4095
# Use a smart method for 1.6 and above servers
4097
self._set_last_revision_descendant(revision_id, other_branch,
4098
allow_diverged=True, allow_overwrite_descendant=True)
4100
except errors.UnknownSmartMethod:
4101
medium._remember_remote_is_before((1, 6))
4102
self._clear_cached_state_of_remote_branch_only()
4103
graph = self.repository.get_graph()
4104
(last_revno, last_revid) = self.last_revision_info()
4105
known_revision_ids = [
4106
(last_revid, last_revno),
4107
(_mod_revision.NULL_REVISION, 0),
4109
if last_rev is not None:
4110
if not graph.is_ancestor(last_rev, revision_id):
4111
# our previous tip is not merged into stop_revision
4112
raise errors.DivergedBranches(self, other_branch)
4113
revno = graph.find_distance_to_null(
4114
revision_id, known_revision_ids)
4115
self.set_last_revision_info(revno, revision_id)
2587
medium = self._client._medium
2588
if not medium._is_remote_before((1, 6)):
2589
# Use a smart method for 1.6 and above servers
2591
self._set_last_revision_descendant(revision_id, other_branch,
2592
allow_diverged=True, allow_overwrite_descendant=True)
2594
except errors.UnknownSmartMethod:
2595
medium._remember_remote_is_before((1, 6))
2596
self._clear_cached_state_of_remote_branch_only()
2597
self.set_revision_history(self._lefthand_history(revision_id,
2598
last_rev=last_rev,other_branch=other_branch))
4117
2600
def set_push_location(self, location):
4118
self._set_config_location('push_location', location)
4120
def heads_to_fetch(self):
4121
if self._format._use_default_local_heads_to_fetch():
4122
# We recognise this format, and its heads-to-fetch implementation
4123
# is the default one (tip + tags). In this case it's cheaper to
4124
# just use the default implementation rather than a special RPC as
4125
# the tip and tags data is cached.
4126
return branch.Branch.heads_to_fetch(self)
4127
medium = self._client._medium
4128
if medium._is_remote_before((2, 4)):
4129
return self._vfs_heads_to_fetch()
4131
return self._rpc_heads_to_fetch()
4132
except errors.UnknownSmartMethod:
4133
medium._remember_remote_is_before((2, 4))
4134
return self._vfs_heads_to_fetch()
4136
def _rpc_heads_to_fetch(self):
4137
response = self._call(b'Branch.heads_to_fetch', self._remote_path())
4138
if len(response) != 2:
4139
raise errors.UnexpectedSmartServerResponse(response)
4140
must_fetch, if_present_fetch = response
4141
return set(must_fetch), set(if_present_fetch)
4143
def _vfs_heads_to_fetch(self):
4145
return self._real_branch.heads_to_fetch()
4147
def reconcile(self, thorough=True):
4148
"""Make sure the data stored in this branch is consistent."""
4149
from .reconcile import BranchReconciler
4150
with self.lock_write():
4151
reconciler = BranchReconciler(self, thorough=thorough)
4152
return reconciler.reconcile()
4154
def get_reference_info(self, file_id):
4155
"""Get the tree_path and branch_location for a tree reference."""
4156
if not self._format.supports_reference_locations:
4157
raise errors.UnsupportedOperation(self.get_reference_info, self)
4158
return self._get_all_reference_info().get(file_id, (None, None))
4160
def set_reference_info(self, file_id, branch_location, tree_path=None):
4161
"""Set the branch location to use for a tree reference."""
4162
if not self._format.supports_reference_locations:
4163
raise errors.UnsupportedOperation(self.set_reference_info, self)
4165
self._real_branch.set_reference_info(
4166
file_id, branch_location, tree_path)
4168
def _set_all_reference_info(self, reference_info):
4169
if not self._format.supports_reference_locations:
4170
raise errors.UnsupportedOperation(self.set_reference_info, self)
4172
self._real_branch._set_all_reference_info(reference_info)
4174
def _get_all_reference_info(self):
4175
if not self._format.supports_reference_locations:
4178
response, handler = self._call_expecting_body(
4179
b'Branch.get_all_reference_info', self._remote_path())
4180
except errors.UnknownSmartMethod:
4182
return self._real_branch._get_all_reference_info()
4183
if len(response) and response[0] != b'ok':
4184
raise errors.UnexpectedSmartServerResponse(response)
4186
for (f, u, p) in bencode.bdecode(handler.read_body_bytes()):
4187
ret[f] = (u.decode('utf-8'), p.decode('utf-8') if p else None)
4190
def reference_parent(self, file_id, path, possible_transports=None):
4191
"""Return the parent branch for a tree-reference.
4193
:param path: The path of the nested tree in the tree
4194
:return: A branch associated with the nested tree
4196
branch_location = self.get_reference_info(file_id)[0]
4197
if branch_location is None:
4199
return branch.Branch.open_from_transport(
4200
self.controldir.root_transport.clone(path),
4201
possible_transports=possible_transports)
4202
except errors.NotBranchError:
4204
return branch.Branch.open(
4206
urlutils.strip_segment_parameters(self.user_url), branch_location),
4207
possible_transports=possible_transports)
2602
return self._real_branch.set_push_location(location)
4210
2605
class RemoteConfig(object):
4275
2660
medium = self._branch._client._medium
4276
2661
if medium._is_remote_before((1, 14)):
4277
2662
return self._vfs_set_option(value, name, section)
4278
if isinstance(value, dict):
4279
if medium._is_remote_before((2, 2)):
4280
return self._vfs_set_option(value, name, section)
4281
return self._set_config_option_dict(value, name, section)
4283
return self._set_config_option(value, name, section)
4285
def _set_config_option(self, value, name, section):
4286
if isinstance(value, (bool, int)):
4288
elif isinstance(value, str):
4291
raise TypeError(value)
4293
2664
path = self._branch._remote_path()
4294
response = self._branch._client.call(b'Branch.set_config_option',
4295
path, self._branch._lock_token, self._branch._repo_lock_token,
4296
value.encode('utf-8'), name.encode('utf-8'),
4297
(section or '').encode('utf-8'))
2665
response = self._branch._client.call('Branch.set_config_option',
2666
path, self._branch._lock_token, self._branch._repo_lock_token,
2667
value.encode('utf8'), name, section or '')
4298
2668
except errors.UnknownSmartMethod:
4299
medium = self._branch._client._medium
4300
2669
medium._remember_remote_is_before((1, 14))
4301
2670
return self._vfs_set_option(value, name, section)
4302
2671
if response != ():
4303
2672
raise errors.UnexpectedSmartServerResponse(response)
4305
def _serialize_option_dict(self, option_dict):
4307
for key, value in option_dict.items():
4308
if isinstance(key, str):
4309
key = key.encode('utf8')
4310
if isinstance(value, str):
4311
value = value.encode('utf8')
4312
utf8_dict[key] = value
4313
return bencode.bencode(utf8_dict)
4315
def _set_config_option_dict(self, value, name, section):
4317
path = self._branch._remote_path()
4318
serialised_dict = self._serialize_option_dict(value)
4319
response = self._branch._client.call(
4320
b'Branch.set_config_option_dict',
4321
path, self._branch._lock_token, self._branch._repo_lock_token,
4322
serialised_dict, name.encode('utf-8'), (section or '').encode('utf-8'))
4323
except errors.UnknownSmartMethod:
4324
medium = self._branch._client._medium
4325
medium._remember_remote_is_before((2, 2))
4326
return self._vfs_set_option(value, name, section)
4328
raise errors.UnexpectedSmartServerResponse(response)
4330
2674
def _real_object(self):
4331
2675
self._branch._ensure_real()
4332
2676
return self._branch._real_branch
4392
2742
def find(name):
4394
2744
return context[name]
4396
mutter('Missing key \'%s\' in context %r', name, context)
2745
except KeyError, key_err:
2746
mutter('Missing key %r in context %r', key_err.args[0], context)
4399
2748
def get_path():
4400
2749
"""Get the path from the context if present, otherwise use first error
4404
2753
return context['path']
2754
except KeyError, key_err:
4407
return err.error_args[0].decode('utf-8')
4409
mutter('Missing key \'path\' in context %r', context)
2756
return err.error_args[0]
2757
except IndexError, idx_err:
2759
'Missing key %r in context %r', key_err.args[0], context)
4411
if not isinstance(err.error_verb, bytes):
4412
raise TypeError(err.error_verb)
4414
translator = error_translators.get(err.error_verb)
4418
raise translator(err, find, get_path)
4420
translator = no_context_error_translators.get(err.error_verb)
4422
raise errors.UnknownErrorFromSmartServer(err)
4424
raise translator(err)
4427
error_translators.register(b'NoSuchRevision',
4428
lambda err, find, get_path: NoSuchRevision(
4429
find('branch'), err.error_args[0]))
4430
error_translators.register(b'nosuchrevision',
4431
lambda err, find, get_path: NoSuchRevision(
4432
find('repository'), err.error_args[0]))
4433
error_translators.register(
4434
b'revno-outofbounds',
4435
lambda err, find, get_path: errors.RevnoOutOfBounds(
4436
err.error_args[0], (err.error_args[1], err.error_args[2])))
4439
def _translate_nobranch_error(err, find, get_path):
4440
if len(err.error_args) >= 1:
4441
extra = err.error_args[0].decode('utf-8')
4444
return errors.NotBranchError(path=find('bzrdir').root_transport.base,
4448
error_translators.register(b'nobranch', _translate_nobranch_error)
4449
error_translators.register(b'norepository',
4450
lambda err, find, get_path: errors.NoRepositoryPresent(
4452
error_translators.register(b'UnlockableTransport',
4453
lambda err, find, get_path: errors.UnlockableTransport(
4454
find('bzrdir').root_transport))
4455
error_translators.register(b'TokenMismatch',
4456
lambda err, find, get_path: errors.TokenMismatch(
4457
find('token'), '(remote token)'))
4458
error_translators.register(b'Diverged',
4459
lambda err, find, get_path: errors.DivergedBranches(
4460
find('branch'), find('other_branch')))
4461
error_translators.register(b'NotStacked',
4462
lambda err, find, get_path: errors.NotStacked(branch=find('branch')))
4465
def _translate_PermissionDenied(err, find, get_path):
4467
if len(err.error_args) >= 2:
4468
extra = err.error_args[1].decode('utf-8')
4471
return errors.PermissionDenied(path, extra=extra)
4474
error_translators.register(b'PermissionDenied', _translate_PermissionDenied)
4475
error_translators.register(b'ReadError',
4476
lambda err, find, get_path: errors.ReadError(get_path()))
4477
error_translators.register(b'NoSuchFile',
4478
lambda err, find, get_path: errors.NoSuchFile(get_path()))
4479
error_translators.register(b'TokenLockingNotSupported',
4480
lambda err, find, get_path: errors.TokenLockingNotSupported(
4481
find('repository')))
4482
error_translators.register(b'UnsuspendableWriteGroup',
4483
lambda err, find, get_path: errors.UnsuspendableWriteGroup(
4484
repository=find('repository')))
4485
error_translators.register(b'UnresumableWriteGroup',
4486
lambda err, find, get_path: errors.UnresumableWriteGroup(
4487
repository=find('repository'), write_groups=err.error_args[0],
4488
reason=err.error_args[1]))
4489
error_translators.register(b'AlreadyControlDir',
4490
lambda err, find, get_path: errors.AlreadyControlDirError(get_path()))
4492
no_context_error_translators.register(b'GhostRevisionsHaveNoRevno',
4493
lambda err: errors.GhostRevisionsHaveNoRevno(*err.error_args))
4494
no_context_error_translators.register(b'IncompatibleRepositories',
4495
lambda err: errors.IncompatibleRepositories(
4496
err.error_args[0].decode('utf-8'), err.error_args[1].decode('utf-8'), err.error_args[2].decode('utf-8')))
4497
no_context_error_translators.register(b'LockContention',
4498
lambda err: errors.LockContention('(remote lock)'))
4499
no_context_error_translators.register(b'LockFailed',
4500
lambda err: errors.LockFailed(err.error_args[0].decode('utf-8'), err.error_args[1].decode('utf-8')))
4501
no_context_error_translators.register(b'TipChangeRejected',
4502
lambda err: errors.TipChangeRejected(err.error_args[0].decode('utf8')))
4503
no_context_error_translators.register(b'UnstackableBranchFormat',
4504
lambda err: branch.UnstackableBranchFormat(*err.error_args))
4505
no_context_error_translators.register(b'UnstackableRepositoryFormat',
4506
lambda err: errors.UnstackableRepositoryFormat(*err.error_args))
4507
no_context_error_translators.register(b'FileExists',
4508
lambda err: errors.FileExists(err.error_args[0].decode('utf-8')))
4509
no_context_error_translators.register(b'DirectoryNotEmpty',
4510
lambda err: errors.DirectoryNotEmpty(err.error_args[0].decode('utf-8')))
4511
no_context_error_translators.register(b'UnknownFormat',
4512
lambda err: errors.UnknownFormatError(
4513
err.error_args[0].decode('ascii'), err.error_args[0].decode('ascii')))
4514
no_context_error_translators.register(b'InvalidURL',
4515
lambda err: urlutils.InvalidURL(
4516
err.error_args[0].decode('utf-8'), err.error_args[1].decode('ascii')))
4519
def _translate_short_readv_error(err):
4520
args = err.error_args
4521
return errors.ShortReadvError(
4522
args[0].decode('utf-8'),
4523
int(args[1].decode('ascii')), int(args[2].decode('ascii')),
4524
int(args[3].decode('ascii')))
4527
no_context_error_translators.register(b'ShortReadvError',
4528
_translate_short_readv_error)
4531
def _translate_unicode_error(err):
4532
encoding = err.error_args[0].decode('ascii')
4533
val = err.error_args[1].decode('utf-8')
4534
start = int(err.error_args[2].decode('ascii'))
4535
end = int(err.error_args[3].decode('ascii'))
4536
reason = err.error_args[4].decode('utf-8')
4537
if val.startswith('u:'):
4538
val = val[2:].decode('utf-8')
4539
elif val.startswith('s:'):
4540
val = val[2:].decode('base64')
4541
if err.error_verb == 'UnicodeDecodeError':
4542
raise UnicodeDecodeError(encoding, val, start, end, reason)
4543
elif err.error_verb == 'UnicodeEncodeError':
4544
raise UnicodeEncodeError(encoding, val, start, end, reason)
4547
no_context_error_translators.register(b'UnicodeEncodeError',
4548
_translate_unicode_error)
4549
no_context_error_translators.register(b'UnicodeDecodeError',
4550
_translate_unicode_error)
4551
no_context_error_translators.register(b'ReadOnlyError',
4552
lambda err: errors.TransportNotPossible('readonly transport'))
4553
no_context_error_translators.register(b'MemoryError',
4554
lambda err: errors.BzrError("remote server out of memory\n"
4555
"Retry non-remotely, or contact the server admin for details."))
4556
no_context_error_translators.register(b'RevisionNotPresent',
4557
lambda err: errors.RevisionNotPresent(err.error_args[0].decode('utf-8'), err.error_args[1].decode('utf-8')))
4559
no_context_error_translators.register(b'BzrCheckError',
4560
lambda err: errors.BzrCheckError(msg=err.error_args[0].decode('utf-8')))
2762
if err.error_verb == 'IncompatibleRepositories':
2763
raise errors.IncompatibleRepositories(err.error_args[0],
2764
err.error_args[1], err.error_args[2])
2765
elif err.error_verb == 'NoSuchRevision':
2766
raise NoSuchRevision(find('branch'), err.error_args[0])
2767
elif err.error_verb == 'nosuchrevision':
2768
raise NoSuchRevision(find('repository'), err.error_args[0])
2769
elif err.error_tuple == ('nobranch',):
2770
raise errors.NotBranchError(path=find('bzrdir').root_transport.base)
2771
elif err.error_verb == 'norepository':
2772
raise errors.NoRepositoryPresent(find('bzrdir'))
2773
elif err.error_verb == 'LockContention':
2774
raise errors.LockContention('(remote lock)')
2775
elif err.error_verb == 'UnlockableTransport':
2776
raise errors.UnlockableTransport(find('bzrdir').root_transport)
2777
elif err.error_verb == 'LockFailed':
2778
raise errors.LockFailed(err.error_args[0], err.error_args[1])
2779
elif err.error_verb == 'TokenMismatch':
2780
raise errors.TokenMismatch(find('token'), '(remote token)')
2781
elif err.error_verb == 'Diverged':
2782
raise errors.DivergedBranches(find('branch'), find('other_branch'))
2783
elif err.error_verb == 'TipChangeRejected':
2784
raise errors.TipChangeRejected(err.error_args[0].decode('utf8'))
2785
elif err.error_verb == 'UnstackableBranchFormat':
2786
raise errors.UnstackableBranchFormat(*err.error_args)
2787
elif err.error_verb == 'UnstackableRepositoryFormat':
2788
raise errors.UnstackableRepositoryFormat(*err.error_args)
2789
elif err.error_verb == 'NotStacked':
2790
raise errors.NotStacked(branch=find('branch'))
2791
elif err.error_verb == 'PermissionDenied':
2793
if len(err.error_args) >= 2:
2794
extra = err.error_args[1]
2797
raise errors.PermissionDenied(path, extra=extra)
2798
elif err.error_verb == 'ReadError':
2800
raise errors.ReadError(path)
2801
elif err.error_verb == 'NoSuchFile':
2803
raise errors.NoSuchFile(path)
2804
elif err.error_verb == 'FileExists':
2805
raise errors.FileExists(err.error_args[0])
2806
elif err.error_verb == 'DirectoryNotEmpty':
2807
raise errors.DirectoryNotEmpty(err.error_args[0])
2808
elif err.error_verb == 'ShortReadvError':
2809
args = err.error_args
2810
raise errors.ShortReadvError(
2811
args[0], int(args[1]), int(args[2]), int(args[3]))
2812
elif err.error_verb in ('UnicodeEncodeError', 'UnicodeDecodeError'):
2813
encoding = str(err.error_args[0]) # encoding must always be a string
2814
val = err.error_args[1]
2815
start = int(err.error_args[2])
2816
end = int(err.error_args[3])
2817
reason = str(err.error_args[4]) # reason must always be a string
2818
if val.startswith('u:'):
2819
val = val[2:].decode('utf-8')
2820
elif val.startswith('s:'):
2821
val = val[2:].decode('base64')
2822
if err.error_verb == 'UnicodeDecodeError':
2823
raise UnicodeDecodeError(encoding, val, start, end, reason)
2824
elif err.error_verb == 'UnicodeEncodeError':
2825
raise UnicodeEncodeError(encoding, val, start, end, reason)
2826
elif err.error_verb == 'ReadOnlyError':
2827
raise errors.TransportNotPossible('readonly transport')
2828
raise errors.UnknownErrorFromSmartServer(err)