105
74
return self._client.call_with_body_bytes_expecting_body(
106
75
method, args, body_bytes)
107
except errors.ErrorFromSmartServer as err:
76
except errors.ErrorFromSmartServer, err:
108
77
self._translate_error(err, **err_context)
111
80
def response_tuple_to_repo_format(response):
112
81
"""Convert a response tuple describing a repository format to a format."""
113
82
format = RemoteRepositoryFormat()
114
format._rich_root_data = (response[0] == b'yes')
115
format._supports_tree_reference = (response[1] == b'yes')
116
format._supports_external_lookups = (response[2] == b'yes')
83
format._rich_root_data = (response[0] == 'yes')
84
format._supports_tree_reference = (response[1] == 'yes')
85
format._supports_external_lookups = (response[2] == 'yes')
117
86
format._network_name = response[3]
121
# Note that RemoteBzrDirProber lives in breezy.bzrdir so breezy.bzr.remote
122
# does not have to be imported unless a remote format is involved.
124
class RemoteBzrDirFormat(_mod_bzrdir.BzrDirMetaFormat1):
125
"""Format representing bzrdirs accessed via a smart server"""
127
supports_workingtrees = False
129
colocated_branches = False
132
_mod_bzrdir.BzrDirMetaFormat1.__init__(self)
133
# XXX: It's a bit ugly that the network name is here, because we'd
134
# like to believe that format objects are stateless or at least
135
# immutable, However, we do at least avoid mutating the name after
136
# it's returned. See <https://bugs.launchpad.net/bzr/+bug/504102>
137
self._network_name = None
140
return "%s(_network_name=%r)" % (self.__class__.__name__,
143
def get_format_description(self):
144
if self._network_name:
146
real_format = controldir.network_format_registry.get(
151
return 'Remote: ' + real_format.get_format_description()
152
return 'bzr remote bzrdir'
154
def get_format_string(self):
155
raise NotImplementedError(self.get_format_string)
157
def network_name(self):
158
if self._network_name:
159
return self._network_name
161
raise AssertionError("No network name set.")
163
def initialize_on_transport(self, transport):
165
# hand off the request to the smart server
166
client_medium = transport.get_smart_medium()
167
except errors.NoSmartMedium:
168
# TODO: lookup the local format from a server hint.
169
local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
170
return local_dir_format.initialize_on_transport(transport)
171
client = _SmartClient(client_medium)
172
path = client.remote_path_from_transport(transport)
174
response = client.call(b'BzrDirFormat.initialize', path)
175
except errors.ErrorFromSmartServer as err:
176
_translate_error(err, path=path)
177
if response[0] != b'ok':
178
raise errors.SmartProtocolError(
179
'unexpected response code %s' % (response,))
180
format = RemoteBzrDirFormat()
181
self._supply_sub_formats_to(format)
182
return RemoteBzrDir(transport, format)
184
def parse_NoneTrueFalse(self, arg):
191
raise AssertionError("invalid arg %r" % arg)
193
def _serialize_NoneTrueFalse(self, arg):
200
def _serialize_NoneString(self, arg):
203
def initialize_on_transport_ex(self, transport, use_existing_dir=False,
204
create_prefix=False, force_new_repo=False, stacked_on=None,
205
stack_on_pwd=None, repo_format_name=None, make_working_trees=None,
208
# hand off the request to the smart server
209
client_medium = transport.get_smart_medium()
210
except errors.NoSmartMedium:
213
# Decline to open it if the server doesn't support our required
214
# version (3) so that the VFS-based transport will do it.
215
if client_medium.should_probe():
217
server_version = client_medium.protocol_version()
218
if server_version != '2':
222
except errors.SmartProtocolError:
223
# Apparently there's no usable smart server there, even though
224
# the medium supports the smart protocol.
229
client = _SmartClient(client_medium)
230
path = client.remote_path_from_transport(transport)
231
if client_medium._is_remote_before((1, 16)):
234
# TODO: lookup the local format from a server hint.
235
local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
236
self._supply_sub_formats_to(local_dir_format)
237
return local_dir_format.initialize_on_transport_ex(transport,
238
use_existing_dir=use_existing_dir, create_prefix=create_prefix,
239
force_new_repo=force_new_repo, stacked_on=stacked_on,
240
stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
241
make_working_trees=make_working_trees, shared_repo=shared_repo,
243
return self._initialize_on_transport_ex_rpc(client, path, transport,
244
use_existing_dir, create_prefix, force_new_repo, stacked_on,
245
stack_on_pwd, repo_format_name, make_working_trees, shared_repo)
247
def _initialize_on_transport_ex_rpc(self, client, path, transport,
248
use_existing_dir, create_prefix, force_new_repo, stacked_on,
249
stack_on_pwd, repo_format_name, make_working_trees, shared_repo):
251
args.append(self._serialize_NoneTrueFalse(use_existing_dir))
252
args.append(self._serialize_NoneTrueFalse(create_prefix))
253
args.append(self._serialize_NoneTrueFalse(force_new_repo))
254
args.append(self._serialize_NoneString(stacked_on))
255
# stack_on_pwd is often/usually our transport
258
stack_on_pwd = transport.relpath(stack_on_pwd).encode('utf-8')
261
except errors.PathNotChild:
263
args.append(self._serialize_NoneString(stack_on_pwd))
264
args.append(self._serialize_NoneString(repo_format_name))
265
args.append(self._serialize_NoneTrueFalse(make_working_trees))
266
args.append(self._serialize_NoneTrueFalse(shared_repo))
267
request_network_name = self._network_name or \
268
_mod_bzrdir.BzrDirFormat.get_default_format().network_name()
270
response = client.call(b'BzrDirFormat.initialize_ex_1.16',
271
request_network_name, path, *args)
272
except errors.UnknownSmartMethod:
273
client._medium._remember_remote_is_before((1, 16))
274
local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
275
self._supply_sub_formats_to(local_dir_format)
276
return local_dir_format.initialize_on_transport_ex(transport,
277
use_existing_dir=use_existing_dir, create_prefix=create_prefix,
278
force_new_repo=force_new_repo, stacked_on=stacked_on,
279
stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
280
make_working_trees=make_working_trees, shared_repo=shared_repo,
282
except errors.ErrorFromSmartServer as err:
283
_translate_error(err, path=path.decode('utf-8'))
284
repo_path = response[0]
285
bzrdir_name = response[6]
286
require_stacking = response[7]
287
require_stacking = self.parse_NoneTrueFalse(require_stacking)
288
format = RemoteBzrDirFormat()
289
format._network_name = bzrdir_name
290
self._supply_sub_formats_to(format)
291
bzrdir = RemoteBzrDir(transport, format, _client=client)
293
repo_format = response_tuple_to_repo_format(response[1:])
294
if repo_path == b'.':
296
repo_path = repo_path.decode('utf-8')
298
repo_bzrdir_format = RemoteBzrDirFormat()
299
repo_bzrdir_format._network_name = response[5]
300
repo_bzr = RemoteBzrDir(transport.clone(repo_path),
304
final_stack = response[8] or None
306
final_stack = final_stack.decode('utf-8')
307
final_stack_pwd = response[9] or None
309
final_stack_pwd = urlutils.join(
310
transport.base, final_stack_pwd.decode('utf-8'))
311
remote_repo = RemoteRepository(repo_bzr, repo_format)
312
if len(response) > 10:
313
# Updated server verb that locks remotely.
314
repo_lock_token = response[10] or None
315
remote_repo.lock_write(repo_lock_token, _skip_rpc=True)
317
remote_repo.dont_leave_lock_in_place()
319
remote_repo.lock_write()
320
policy = _mod_bzrdir.UseExistingRepository(remote_repo,
321
final_stack, final_stack_pwd, require_stacking)
322
policy.acquire_repository()
326
bzrdir._format.set_branch_format(self.get_branch_format())
328
# The repo has already been created, but we need to make sure that
329
# we'll make a stackable branch.
330
bzrdir._format.require_stacking(_skip_repo=True)
331
return remote_repo, bzrdir, require_stacking, policy
333
def _open(self, transport):
334
return RemoteBzrDir(transport, self)
336
def __eq__(self, other):
337
if not isinstance(other, RemoteBzrDirFormat):
339
return self.get_format_description() == other.get_format_description()
341
def __return_repository_format(self):
342
# Always return a RemoteRepositoryFormat object, but if a specific bzr
343
# repository format has been asked for, tell the RemoteRepositoryFormat
344
# that it should use that for init() etc.
345
result = RemoteRepositoryFormat()
346
custom_format = getattr(self, '_repository_format', None)
348
if isinstance(custom_format, RemoteRepositoryFormat):
351
# We will use the custom format to create repositories over the
352
# wire; expose its details like rich_root_data for code to
354
result._custom_format = custom_format
357
def get_branch_format(self):
358
result = _mod_bzrdir.BzrDirMetaFormat1.get_branch_format(self)
359
if not isinstance(result, RemoteBranchFormat):
360
new_result = RemoteBranchFormat()
361
new_result._custom_format = result
363
self.set_branch_format(new_result)
367
repository_format = property(__return_repository_format,
368
_mod_bzrdir.BzrDirMetaFormat1._set_repository_format) # .im_func)
371
class RemoteControlStore(_mod_config.IniFileStore):
372
"""Control store which attempts to use HPSS calls to retrieve control store.
374
Note that this is specific to bzr-based formats.
377
def __init__(self, bzrdir):
378
super(RemoteControlStore, self).__init__()
379
self.controldir = bzrdir
380
self._real_store = None
382
def lock_write(self, token=None):
384
return self._real_store.lock_write(token)
388
return self._real_store.unlock()
391
with self.lock_write():
392
# We need to be able to override the undecorated implementation
393
self.save_without_locking()
395
def save_without_locking(self):
396
super(RemoteControlStore, self).save()
398
def _ensure_real(self):
399
self.controldir._ensure_real()
400
if self._real_store is None:
401
self._real_store = _mod_config.ControlStore(self.controldir)
403
def external_url(self):
404
return urlutils.join(self.branch.user_url, 'control.conf')
406
def _load_content(self):
407
medium = self.controldir._client._medium
408
path = self.controldir._path_for_remote_call(self.controldir._client)
410
response, handler = self.controldir._call_expecting_body(
411
b'BzrDir.get_config_file', path)
412
except errors.UnknownSmartMethod:
414
return self._real_store._load_content()
415
if len(response) and response[0] != b'ok':
416
raise errors.UnexpectedSmartServerResponse(response)
417
return handler.read_body_bytes()
419
def _save_content(self, content):
420
# FIXME JRV 2011-11-22: Ideally this should use a
421
# HPSS call too, but at the moment it is not possible
422
# to write lock control directories.
424
return self._real_store._save_content(content)
427
class RemoteBzrDir(_mod_bzrdir.BzrDir, _RpcHelper):
90
# Note: RemoteBzrDirFormat is in bzrdir.py
92
class RemoteBzrDir(BzrDir, _RpcHelper):
428
93
"""Control directory on a remote server, accessed via bzr:// or similar."""
430
95
def __init__(self, transport, format, _client=None, _force_probe=False):
564
187
medium = self._client._medium
565
188
if medium._is_remote_before((1, 13)):
566
189
return self._vfs_cloning_metadir(require_stacking=require_stacking)
567
verb = b'BzrDir.cloning_metadir'
190
verb = 'BzrDir.cloning_metadir'
568
191
if require_stacking:
572
195
path = self._path_for_remote_call(self._client)
574
197
response = self._call(verb, path, stacking)
575
198
except errors.UnknownSmartMethod:
576
199
medium._remember_remote_is_before((1, 13))
577
200
return self._vfs_cloning_metadir(require_stacking=require_stacking)
578
except errors.UnknownErrorFromSmartServer as err:
579
if err.error_tuple != (b'BranchReference',):
201
except errors.UnknownErrorFromSmartServer, err:
202
if err.error_tuple != ('BranchReference',):
581
204
# We need to resolve the branch reference to determine the
582
205
# cloning_metadir. This causes unnecessary RPCs to open the
583
206
# referenced branch (and bzrdir, etc) but only when the caller
584
207
# didn't already resolve the branch reference.
585
208
referenced_branch = self.open_branch()
586
return referenced_branch.controldir.cloning_metadir()
209
return referenced_branch.bzrdir.cloning_metadir()
587
210
if len(response) != 3:
588
211
raise errors.UnexpectedSmartServerResponse(response)
589
212
control_name, repo_name, branch_info = response
590
213
if len(branch_info) != 2:
591
214
raise errors.UnexpectedSmartServerResponse(response)
592
215
branch_ref, branch_name = branch_info
594
format = controldir.network_format_registry.get(control_name)
596
raise errors.UnknownFormatError(
597
kind='control', format=control_name)
216
format = bzrdir.network_format_registry.get(control_name)
601
format.repository_format = _mod_repository.network_format_registry.get(
604
raise errors.UnknownFormatError(kind='repository',
606
if branch_ref == b'ref':
218
format.repository_format = repository.network_format_registry.get(
220
if branch_ref == 'ref':
607
221
# XXX: we need possible_transports here to avoid reopening the
608
222
# connection to the referenced location
609
ref_bzrdir = _mod_bzrdir.BzrDir.open(branch_name)
223
ref_bzrdir = BzrDir.open(branch_name)
610
224
branch_format = ref_bzrdir.cloning_metadir().get_branch_format()
611
225
format.set_branch_format(branch_format)
612
elif branch_ref == b'branch':
226
elif branch_ref == 'branch':
615
branch_format = branch.network_format_registry.get(
618
raise errors.UnknownFormatError(kind='branch',
620
format.set_branch_format(branch_format)
228
format.set_branch_format(
229
branch.network_format_registry.get(branch_name))
622
231
raise errors.UnexpectedSmartServerResponse(response)
675
267
def destroy_branch(self, name=None):
676
268
"""See BzrDir.destroy_branch"""
678
name = self._get_selected_branch()
680
raise errors.NoColocatedBranchSupport(self)
681
path = self._path_for_remote_call(self._client)
687
response = self._call(b'BzrDir.destroy_branch', path, *args)
688
except errors.UnknownSmartMethod:
690
self._real_bzrdir.destroy_branch(name=name)
691
self._next_open_branch_result = None
270
self._real_bzrdir.destroy_branch(name=name)
693
271
self._next_open_branch_result = None
694
if response[0] != b'ok':
695
raise SmartProtocolError(
696
'unexpected response code %s' % (response,))
698
def create_workingtree(self, revision_id=None, from_branch=None,
699
accelerator_tree=None, hardlink=False):
273
def create_workingtree(self, revision_id=None, from_branch=None):
700
274
raise errors.NotLocalUrl(self.transport.base)
702
def find_branch_format(self, name=None):
276
def find_branch_format(self):
703
277
"""Find the branch 'format' for this bzrdir.
705
279
This might be a synthetic object for e.g. RemoteBranch and SVN.
707
b = self.open_branch(name=name)
281
b = self.open_branch()
710
def branch_names(self):
711
path = self._path_for_remote_call(self._client)
713
response, handler = self._call_expecting_body(
714
b'BzrDir.get_branches', path)
715
except errors.UnknownSmartMethod:
717
return self._real_bzrdir.branch_names()
718
if response[0] != b"success":
719
raise errors.UnexpectedSmartServerResponse(response)
720
body = bencode.bdecode(handler.read_body_bytes())
722
for name, value in viewitems(body):
723
name = name.decode('utf-8')
727
def get_branches(self, possible_transports=None, ignore_fallbacks=False):
728
path = self._path_for_remote_call(self._client)
730
response, handler = self._call_expecting_body(
731
b'BzrDir.get_branches', path)
732
except errors.UnknownSmartMethod:
734
return self._real_bzrdir.get_branches()
735
if response[0] != b"success":
736
raise errors.UnexpectedSmartServerResponse(response)
737
body = bencode.bdecode(handler.read_body_bytes())
739
for name, value in viewitems(body):
740
name = name.decode('utf-8')
741
ret[name] = self._open_branch(
742
name, value[0].decode('ascii'), value[1],
743
possible_transports=possible_transports,
744
ignore_fallbacks=ignore_fallbacks)
747
def set_branch_reference(self, target_branch, name=None):
748
"""See BzrDir.set_branch_reference()."""
750
name = self._get_selected_branch()
752
raise errors.NoColocatedBranchSupport(self)
754
return self._real_bzrdir.set_branch_reference(target_branch, name=name)
756
def get_branch_reference(self, name=None):
284
def get_branch_reference(self):
757
285
"""See BzrDir.get_branch_reference()."""
759
name = self._get_selected_branch()
761
raise errors.NoColocatedBranchSupport(self)
762
286
response = self._get_branch_reference()
763
287
if response[0] == 'ref':
764
return response[1].decode('utf-8')
768
292
def _get_branch_reference(self):
769
"""Get branch reference information
771
:return: Tuple with (kind, location_or_format)
772
if kind == 'ref', then location_or_format contains a location
773
otherwise, it contains a format name
775
293
path = self._path_for_remote_call(self._client)
776
294
medium = self._client._medium
777
295
candidate_calls = [
778
(b'BzrDir.open_branchV3', (2, 1)),
779
(b'BzrDir.open_branchV2', (1, 13)),
780
(b'BzrDir.open_branch', None),
296
('BzrDir.open_branchV3', (2, 1)),
297
('BzrDir.open_branchV2', (1, 13)),
298
('BzrDir.open_branch', None),
782
300
for verb, required_version in candidate_calls:
783
301
if required_version and medium._is_remote_before(required_version):
790
308
medium._remember_remote_is_before(required_version)
793
if verb == b'BzrDir.open_branch':
794
if response[0] != b'ok':
311
if verb == 'BzrDir.open_branch':
312
if response[0] != 'ok':
795
313
raise errors.UnexpectedSmartServerResponse(response)
796
if response[1] != b'':
314
if response[1] != '':
797
315
return ('ref', response[1])
799
return ('branch', b'')
800
if response[0] not in (b'ref', b'branch'):
317
return ('branch', '')
318
if response[0] not in ('ref', 'branch'):
801
319
raise errors.UnexpectedSmartServerResponse(response)
802
return (response[0].decode('ascii'), response[1])
804
def _get_tree_branch(self, name=None):
322
def _get_tree_branch(self):
805
323
"""See BzrDir._get_tree_branch()."""
806
return None, self.open_branch(name=name)
324
return None, self.open_branch()
808
def _open_branch(self, name, kind, location_or_format,
809
ignore_fallbacks=False, possible_transports=None):
326
def open_branch(self, name=None, unsupported=False,
327
ignore_fallbacks=False):
329
raise NotImplementedError('unsupported flag support not implemented yet.')
330
if self._next_open_branch_result is not None:
331
# See create_branch for details.
332
result = self._next_open_branch_result
333
self._next_open_branch_result = None
335
response = self._get_branch_reference()
336
if response[0] == 'ref':
811
337
# a branch reference, use the existing BranchReference logic.
812
338
format = BranchReferenceFormat()
813
ref_loc = urlutils.join(self.user_url, location_or_format.decode('utf-8'))
814
339
return format.open(self, name=name, _found=True,
816
ignore_fallbacks=ignore_fallbacks,
817
possible_transports=possible_transports)
818
branch_format_name = location_or_format
340
location=response[1], ignore_fallbacks=ignore_fallbacks)
341
branch_format_name = response[1]
819
342
if not branch_format_name:
820
343
branch_format_name = None
821
344
format = RemoteBranchFormat(network_name=branch_format_name)
822
345
return RemoteBranch(self, self.find_repository(), format=format,
823
setup_stacking=not ignore_fallbacks, name=name,
824
possible_transports=possible_transports)
826
def open_branch(self, name=None, unsupported=False,
827
ignore_fallbacks=False, possible_transports=None):
829
name = self._get_selected_branch()
831
raise errors.NoColocatedBranchSupport(self)
833
raise NotImplementedError(
834
'unsupported flag support not implemented yet.')
835
if self._next_open_branch_result is not None:
836
# See create_branch for details.
837
result = self._next_open_branch_result
838
self._next_open_branch_result = None
840
response = self._get_branch_reference()
841
return self._open_branch(name, response[0], response[1],
842
possible_transports=possible_transports,
843
ignore_fallbacks=ignore_fallbacks)
346
setup_stacking=not ignore_fallbacks, name=name)
845
348
def _open_repo_v1(self, path):
846
verb = b'BzrDir.find_repository'
349
verb = 'BzrDir.find_repository'
847
350
response = self._call(verb, path)
848
if response[0] != b'ok':
351
if response[0] != 'ok':
849
352
raise errors.UnexpectedSmartServerResponse(response)
850
353
# servers that only support the v1 method don't support external
851
354
# references either.
852
355
self._ensure_real()
853
356
repo = self._real_bzrdir.open_repository()
854
response = response + (b'no', repo._format.network_name())
357
response = response + ('no', repo._format.network_name())
855
358
return response, repo
857
360
def _open_repo_v2(self, path):
858
verb = b'BzrDir.find_repositoryV2'
361
verb = 'BzrDir.find_repositoryV2'
859
362
response = self._call(verb, path)
860
if response[0] != b'ok':
363
if response[0] != 'ok':
861
364
raise errors.UnexpectedSmartServerResponse(response)
862
365
self._ensure_real()
863
366
repo = self._real_bzrdir.open_repository()
956
442
"""Upgrading of remote bzrdirs is not supported yet."""
959
def needs_format_conversion(self, format):
445
def needs_format_conversion(self, format=None):
960
446
"""Upgrading of remote bzrdirs is not supported yet."""
448
symbol_versioning.warn(symbol_versioning.deprecated_in((1, 13, 0))
449
% 'needs_format_conversion(format=None)')
452
def clone(self, url, revision_id=None, force_new_repo=False,
453
preserve_stacking=False):
455
return self._real_bzrdir.clone(url, revision_id=revision_id,
456
force_new_repo=force_new_repo, preserve_stacking=preserve_stacking)
963
458
def _get_config(self):
964
459
return RemoteBzrDirConfig(self)
966
def _get_config_store(self):
967
return RemoteControlStore(self)
970
class RemoteInventoryTree(InventoryRevisionTree):
972
def __init__(self, repository, inv, revision_id):
973
super(RemoteInventoryTree, self).__init__(repository, inv, revision_id)
975
def archive(self, format, name, root=None, subdir=None, force_mtime=None):
976
ret = self._repository._revision_archive(
977
self.get_revision_id(), format, name, root, subdir,
978
force_mtime=force_mtime)
980
return super(RemoteInventoryTree, self).archive(
981
format, name, root, subdir, force_mtime=force_mtime)
984
def annotate_iter(self, path,
985
default_revision=_mod_revision.CURRENT_REVISION):
986
"""Return an iterator of revision_id, line tuples.
988
For working trees (and mutable trees in general), the special
989
revision_id 'current:' will be used for lines that are new in this
990
tree, e.g. uncommitted changes.
991
:param default_revision: For lines that don't match a basis, mark them
992
with this revision id. Not all implementations will make use of
995
ret = self._repository._annotate_file_revision(
996
self.get_revision_id(), path, file_id=None,
997
default_revision=default_revision)
999
return super(RemoteInventoryTree, self).annotate_iter(
1000
path, default_revision=default_revision)
1004
class RemoteRepositoryFormat(vf_repository.VersionedFileRepositoryFormat):
462
class RemoteRepositoryFormat(repository.RepositoryFormat):
1005
463
"""Format for repositories accessed over a _SmartClient.
1007
465
Instances of this repository are represented by RemoteRepository
1094
530
self._custom_format.supports_tree_reference
1095
531
return self._supports_tree_reference
1098
def revision_graph_can_have_wrong_parents(self):
1099
if self._revision_graph_can_have_wrong_parents is None:
1101
self._revision_graph_can_have_wrong_parents = \
1102
self._custom_format.revision_graph_can_have_wrong_parents
1103
return self._revision_graph_can_have_wrong_parents
1105
def _vfs_initialize(self, a_controldir, shared):
533
def _vfs_initialize(self, a_bzrdir, shared):
1106
534
"""Helper for common code in initialize."""
1107
535
if self._custom_format:
1108
536
# Custom format requested
1109
result = self._custom_format.initialize(
1110
a_controldir, shared=shared)
537
result = self._custom_format.initialize(a_bzrdir, shared=shared)
1111
538
elif self._creating_bzrdir is not None:
1112
539
# Use the format that the repository we were created to back
1114
541
prior_repo = self._creating_bzrdir.open_repository()
1115
542
prior_repo._ensure_real()
1116
543
result = prior_repo._real_repository._format.initialize(
1117
a_controldir, shared=shared)
544
a_bzrdir, shared=shared)
1119
546
# assume that a_bzr is a RemoteBzrDir but the smart server didn't
1120
547
# support remote initialization.
1121
548
# We delegate to a real object at this point (as RemoteBzrDir
1122
549
# delegate to the repository format which would lead to infinite
1123
# recursion if we just called a_controldir.create_repository.
1124
a_controldir._ensure_real()
1125
result = a_controldir._real_bzrdir.create_repository(shared=shared)
550
# recursion if we just called a_bzrdir.create_repository.
551
a_bzrdir._ensure_real()
552
result = a_bzrdir._real_bzrdir.create_repository(shared=shared)
1126
553
if not isinstance(result, RemoteRepository):
1127
return self.open(a_controldir)
554
return self.open(a_bzrdir)
1131
def initialize(self, a_controldir, shared=False):
558
def initialize(self, a_bzrdir, shared=False):
1132
559
# Being asked to create on a non RemoteBzrDir:
1133
if not isinstance(a_controldir, RemoteBzrDir):
1134
return self._vfs_initialize(a_controldir, shared)
1135
medium = a_controldir._client._medium
560
if not isinstance(a_bzrdir, RemoteBzrDir):
561
return self._vfs_initialize(a_bzrdir, shared)
562
medium = a_bzrdir._client._medium
1136
563
if medium._is_remote_before((1, 13)):
1137
return self._vfs_initialize(a_controldir, shared)
564
return self._vfs_initialize(a_bzrdir, shared)
1138
565
# Creating on a remote bzr dir.
1139
566
# 1) get the network name to use.
1140
567
if self._custom_format:
1142
569
elif self._network_name:
1143
570
network_name = self._network_name
1145
# Select the current breezy default and ask for that.
1146
reference_bzrdir_format = controldir.format_registry.get(
572
# Select the current bzrlib default and ask for that.
573
reference_bzrdir_format = bzrdir.format_registry.get('default')()
1148
574
reference_format = reference_bzrdir_format.repository_format
1149
575
network_name = reference_format.network_name()
1150
576
# 2) try direct creation via RPC
1151
path = a_controldir._path_for_remote_call(a_controldir._client)
1152
verb = b'BzrDir.create_repository'
577
path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
578
verb = 'BzrDir.create_repository'
1154
shared_str = b'True'
1156
shared_str = b'False'
1158
response = a_controldir._call(verb, path, network_name, shared_str)
584
response = a_bzrdir._call(verb, path, network_name, shared_str)
1159
585
except errors.UnknownSmartMethod:
1160
586
# Fallback - use vfs methods
1161
587
medium._remember_remote_is_before((1, 13))
1162
return self._vfs_initialize(a_controldir, shared)
588
return self._vfs_initialize(a_bzrdir, shared)
1164
590
# Turn the response into a RemoteRepository object.
1165
591
format = response_tuple_to_repo_format(response[1:])
1166
592
# Used to support creating a real format instance when needed.
1167
format._creating_bzrdir = a_controldir
1168
remote_repo = RemoteRepository(a_controldir, format)
593
format._creating_bzrdir = a_bzrdir
594
remote_repo = RemoteRepository(a_bzrdir, format)
1169
595
format._creating_repo = remote_repo
1170
596
return remote_repo
1172
def open(self, a_controldir):
1173
if not isinstance(a_controldir, RemoteBzrDir):
1174
raise AssertionError('%r is not a RemoteBzrDir' % (a_controldir,))
1175
return a_controldir.open_repository()
598
def open(self, a_bzrdir):
599
if not isinstance(a_bzrdir, RemoteBzrDir):
600
raise AssertionError('%r is not a RemoteBzrDir' % (a_bzrdir,))
601
return a_bzrdir.open_repository()
1177
603
def _ensure_real(self):
1178
604
if self._custom_format is None:
1180
self._custom_format = _mod_repository.network_format_registry.get(
1183
raise errors.UnknownFormatError(kind='repository',
1184
format=self._network_name)
605
self._custom_format = repository.network_format_registry.get(
1187
609
def _fetch_order(self):
1551
910
# TODO: Move to RepositoryBase and unify with the regular Repository
1552
911
# one; unfortunately the tests rely on slightly different behaviour at
1553
912
# present -- mbp 20090710
1554
return (self.__class__ is other.__class__
1555
and self.controldir.transport.base == other.controldir.transport.base)
913
return (self.__class__ is other.__class__ and
914
self.bzrdir.transport.base == other.bzrdir.transport.base)
1557
916
def get_graph(self, other_repository=None):
1558
917
"""Return the graph for this repository format"""
1559
918
parents_provider = self._make_parents_provider(other_repository)
1560
919
return graph.Graph(parents_provider)
1562
922
def get_known_graph_ancestry(self, revision_ids):
1563
923
"""Return the known graph for a set of revision ids and their ancestors.
1565
with self.lock_read():
1566
revision_graph = dict(((key, value) for key, value in
1567
self.get_graph().iter_ancestry(revision_ids) if value is not None))
1568
revision_graph = _mod_repository._strip_NULL_ghosts(revision_graph)
1569
return graph.KnownGraph(revision_graph)
925
st = static_tuple.StaticTuple
926
revision_keys = [st(r_id).intern() for r_id in revision_ids]
927
known_graph = self.revisions.get_known_graph_ancestry(revision_keys)
928
return graph.GraphThunkIdsToKeys(known_graph)
1571
930
def gather_stats(self, revid=None, committers=None):
1572
931
"""See Repository.gather_stats()."""
1573
path = self.controldir._path_for_remote_call(self._client)
932
path = self.bzrdir._path_for_remote_call(self._client)
1574
933
# revid can be None to indicate no revisions, not just NULL_REVISION
1575
if revid is None or _mod_revision.is_null(revid):
934
if revid is None or revision.is_null(revid):
1578
937
fmt_revid = revid
1579
938
if committers is None or not committers:
1580
fmt_committers = b'no'
939
fmt_committers = 'no'
1582
fmt_committers = b'yes'
941
fmt_committers = 'yes'
1583
942
response_tuple, response_handler = self._call_expecting_body(
1584
b'Repository.gather_stats', path, fmt_revid, fmt_committers)
1585
if response_tuple[0] != b'ok':
943
'Repository.gather_stats', path, fmt_revid, fmt_committers)
944
if response_tuple[0] != 'ok':
1586
945
raise errors.UnexpectedSmartServerResponse(response_tuple)
1588
947
body = response_handler.read_body_bytes()
1590
for line in body.split(b'\n'):
949
for line in body.split('\n'):
1593
key, val_text = line.split(b':')
1594
key = key.decode('ascii')
952
key, val_text = line.split(':')
1595
953
if key in ('revisions', 'size', 'committers'):
1596
954
result[key] = int(val_text)
1597
955
elif key in ('firstrev', 'latestrev'):
1598
values = val_text.split(b' ')[1:]
1599
result[key] = (float(values[0]), int(values[1]))
956
values = val_text.split(' ')[1:]
957
result[key] = (float(values[0]), long(values[1]))
1880
1201
raise errors.UnexpectedSmartServerResponse(response)
1882
1203
def sprout(self, to_bzrdir, revision_id=None):
1883
"""Create a descendent repository for new development.
1885
Unlike clone, this does not copy the settings of the repository.
1887
with self.lock_read():
1888
dest_repo = self._create_sprouting_repo(to_bzrdir, shared=False)
1889
dest_repo.fetch(self, revision_id=revision_id)
1892
def _create_sprouting_repo(self, a_controldir, shared):
1893
if not isinstance(a_controldir._format, self.controldir._format.__class__):
1894
# use target default format.
1895
dest_repo = a_controldir.create_repository()
1897
# Most control formats need the repository to be specifically
1898
# created, but on some old all-in-one formats it's not needed
1900
dest_repo = self._format.initialize(
1901
a_controldir, shared=shared)
1902
except errors.UninitializableFormat:
1903
dest_repo = a_controldir.open_repository()
1204
# TODO: Option to control what format is created?
1206
dest_repo = self._real_repository._format.initialize(to_bzrdir,
1208
dest_repo.fetch(self, revision_id=revision_id)
1904
1209
return dest_repo
1906
# These methods are just thin shims to the VFS object for now.
1211
### These methods are just thin shims to the VFS object for now.
1908
1213
def revision_tree(self, revision_id):
1909
with self.lock_read():
1910
revision_id = _mod_revision.ensure_null(revision_id)
1911
if revision_id == _mod_revision.NULL_REVISION:
1912
return InventoryRevisionTree(self,
1913
Inventory(root_id=None), _mod_revision.NULL_REVISION)
1915
return list(self.revision_trees([revision_id]))[0]
1215
return self._real_repository.revision_tree(revision_id)
1917
1217
def get_serializer_format(self):
1918
path = self.controldir._path_for_remote_call(self._client)
1920
response = self._call(b'VersionedFileRepository.get_serializer_format',
1922
except errors.UnknownSmartMethod:
1924
return self._real_repository.get_serializer_format()
1925
if response[0] != b'ok':
1926
raise errors.UnexpectedSmartServerResponse(response)
1219
return self._real_repository.get_serializer_format()
1929
1221
def get_commit_builder(self, branch, parents, config, timestamp=None,
1930
1222
timezone=None, committer=None, revprops=None,
1931
revision_id=None, lossy=False):
1932
"""Obtain a CommitBuilder for this repository.
1934
:param branch: Branch to commit to.
1935
:param parents: Revision ids of the parents of the new revision.
1936
:param config: Configuration to use.
1937
:param timestamp: Optional timestamp recorded for commit.
1938
:param timezone: Optional timezone for timestamp.
1939
:param committer: Optional committer to set for commit.
1940
:param revprops: Optional dictionary of revision properties.
1941
:param revision_id: Optional revision id.
1942
:param lossy: Whether to discard data that can not be natively
1943
represented, when pushing to a foreign VCS
1945
if self._fallback_repositories and not self._format.supports_chks:
1946
raise errors.BzrError("Cannot commit directly to a stacked branch"
1947
" in pre-2a formats. See "
1948
"https://bugs.launchpad.net/bzr/+bug/375013 for details.")
1949
commit_builder_kls = vf_repository.VersionedFileCommitBuilder
1950
result = commit_builder_kls(self, parents, config,
1951
timestamp, timezone, committer, revprops, revision_id,
1953
self.start_write_group()
1224
# FIXME: It ought to be possible to call this without immediately
1225
# triggering _ensure_real. For now it's the easiest thing to do.
1227
real_repo = self._real_repository
1228
builder = real_repo.get_commit_builder(branch, parents,
1229
config, timestamp=timestamp, timezone=timezone,
1230
committer=committer, revprops=revprops, revision_id=revision_id)
1956
1233
def add_fallback_repository(self, repository):
1957
1234
"""Add a repository to use for looking up data not held locally.
1996
1272
return self._real_repository.add_inventory(revid, inv, parents)
1998
1274
def add_inventory_by_delta(self, basis_revision_id, delta, new_revision_id,
1999
parents, basis_inv=None, propagate_caches=False):
1275
parents, basis_inv=None, propagate_caches=False):
2000
1276
self._ensure_real()
2001
1277
return self._real_repository.add_inventory_by_delta(basis_revision_id,
2002
delta, new_revision_id, parents, basis_inv=basis_inv,
2003
propagate_caches=propagate_caches)
2005
def add_revision(self, revision_id, rev, inv=None):
2006
_mod_revision.check_not_reserved_id(revision_id)
2007
key = (revision_id,)
2008
# check inventory present
2009
if not self.inventories.get_parent_map([key]):
2011
raise errors.WeaveRevisionNotPresent(revision_id,
2014
# yes, this is not suitable for adding with ghosts.
2015
rev.inventory_sha1 = self.add_inventory(revision_id, inv,
2018
rev.inventory_sha1 = self.inventories.get_sha1s([key])[key]
2019
self._add_revision(rev)
2021
def _add_revision(self, rev):
2022
if self._real_repository is not None:
2023
return self._real_repository._add_revision(rev)
2024
lines = self._serializer.write_revision_to_lines(rev)
2025
key = (rev.revision_id,)
2026
parents = tuple((parent,) for parent in rev.parent_ids)
2027
self._write_group_tokens, missing_keys = self._get_sink().insert_stream(
2028
[('revisions', [ChunkedContentFactory(key, parents, None, lines, chunks_are_lines=True)])],
2029
self._format, self._write_group_tokens)
1278
delta, new_revision_id, parents, basis_inv=basis_inv,
1279
propagate_caches=propagate_caches)
1281
def add_revision(self, rev_id, rev, inv=None, config=None):
1283
return self._real_repository.add_revision(
1284
rev_id, rev, inv=inv, config=config)
2031
1287
def get_inventory(self, revision_id):
2032
with self.lock_read():
2033
return list(self.iter_inventories([revision_id]))[0]
2035
def _iter_inventories_rpc(self, revision_ids, ordering):
2036
if ordering is None:
2037
ordering = 'unordered'
2038
path = self.controldir._path_for_remote_call(self._client)
2039
body = b"\n".join(revision_ids)
2040
response_tuple, response_handler = (
2041
self._call_with_body_bytes_expecting_body(
2042
b"VersionedFileRepository.get_inventories",
2043
(path, ordering.encode('ascii')), body))
2044
if response_tuple[0] != b"ok":
2045
raise errors.UnexpectedSmartServerResponse(response_tuple)
2046
deserializer = inventory_delta.InventoryDeltaDeserializer()
2047
byte_stream = response_handler.read_streamed_body()
2048
decoded = smart_repo._byte_stream_to_stream(byte_stream)
2050
# no results whatsoever
2052
src_format, stream = decoded
2053
if src_format.network_name() != self._format.network_name():
2054
raise AssertionError(
2055
"Mismatched RemoteRepository and stream src %r, %r" % (
2056
src_format.network_name(), self._format.network_name()))
2057
# ignore the src format, it's not really relevant
2058
prev_inv = Inventory(root_id=None,
2059
revision_id=_mod_revision.NULL_REVISION)
2060
# there should be just one substream, with inventory deltas
2062
substream_kind, substream = next(stream)
2063
except StopIteration:
2065
if substream_kind != "inventory-deltas":
2066
raise AssertionError(
2067
"Unexpected stream %r received" % substream_kind)
2068
for record in substream:
2069
(parent_id, new_id, versioned_root, tree_references, invdelta) = (
2070
deserializer.parse_text_bytes(record.get_bytes_as("lines")))
2071
if parent_id != prev_inv.revision_id:
2072
raise AssertionError("invalid base %r != %r" % (parent_id,
2073
prev_inv.revision_id))
2074
inv = prev_inv.create_by_apply_delta(invdelta, new_id)
2075
yield inv, inv.revision_id
2078
def _iter_inventories_vfs(self, revision_ids, ordering=None):
2079
1288
self._ensure_real()
2080
return self._real_repository._iter_inventories(revision_ids, ordering)
1289
return self._real_repository.get_inventory(revision_id)
2082
1291
def iter_inventories(self, revision_ids, ordering=None):
2083
"""Get many inventories by revision_ids.
2085
This will buffer some or all of the texts used in constructing the
2086
inventories in memory, but will only parse a single inventory at a
2089
:param revision_ids: The expected revision ids of the inventories.
2090
:param ordering: optional ordering, e.g. 'topological'. If not
2091
specified, the order of revision_ids will be preserved (by
2092
buffering if necessary).
2093
:return: An iterator of inventories.
2095
if ((None in revision_ids) or
2096
(_mod_revision.NULL_REVISION in revision_ids)):
2097
raise ValueError('cannot get null revision inventory')
2098
for inv, revid in self._iter_inventories(revision_ids, ordering):
2100
raise errors.NoSuchRevision(self, revid)
2103
def _iter_inventories(self, revision_ids, ordering=None):
2104
if len(revision_ids) == 0:
2106
missing = set(revision_ids)
2107
if ordering is None:
2108
order_as_requested = True
2110
order = list(revision_ids)
2112
next_revid = order.pop()
2114
order_as_requested = False
2115
if ordering != 'unordered' and self._fallback_repositories:
2116
raise ValueError('unsupported ordering %r' % ordering)
2117
iter_inv_fns = [self._iter_inventories_rpc] + [
2118
fallback._iter_inventories for fallback in
2119
self._fallback_repositories]
2121
for iter_inv in iter_inv_fns:
2122
request = [revid for revid in revision_ids if revid in missing]
2123
for inv, revid in iter_inv(request, ordering):
2126
missing.remove(inv.revision_id)
2127
if ordering != 'unordered':
2131
if order_as_requested:
2132
# Yield as many results as we can while preserving order.
2133
while next_revid in invs:
2134
inv = invs.pop(next_revid)
2135
yield inv, inv.revision_id
2137
next_revid = order.pop()
2139
# We still want to fully consume the stream, just
2140
# in case it is not actually finished at this point
2143
except errors.UnknownSmartMethod:
2144
for inv, revid in self._iter_inventories_vfs(revision_ids, ordering):
2148
if order_as_requested:
2149
if next_revid is not None:
2150
yield None, next_revid
2153
yield invs.get(revid), revid
2156
yield None, missing.pop()
1293
return self._real_repository.iter_inventories(revision_ids, ordering)
2158
1296
def get_revision(self, revision_id):
2159
with self.lock_read():
2160
return self.get_revisions([revision_id])[0]
1298
return self._real_repository.get_revision(revision_id)
2162
1300
def get_transaction(self):
2163
1301
self._ensure_real()
2164
1302
return self._real_repository.get_transaction()
2166
def clone(self, a_controldir, revision_id=None):
2167
with self.lock_read():
2168
dest_repo = self._create_sprouting_repo(
2169
a_controldir, shared=self.is_shared())
2170
self.copy_content_into(dest_repo, revision_id)
1305
def clone(self, a_bzrdir, revision_id=None):
1307
return self._real_repository.clone(a_bzrdir, revision_id=revision_id)
2173
1309
def make_working_trees(self):
2174
1310
"""See Repository.make_working_trees"""
2175
path = self.controldir._path_for_remote_call(self._client)
2177
response = self._call(b'Repository.make_working_trees', path)
2178
except errors.UnknownSmartMethod:
2180
return self._real_repository.make_working_trees()
2181
if response[0] not in (b'yes', b'no'):
2182
raise SmartProtocolError(
2183
'unexpected response code %s' % (response,))
2184
return response[0] == b'yes'
1312
return self._real_repository.make_working_trees()
2186
1314
def refresh_data(self):
2187
"""Re-read any data needed to synchronise with disk.
1315
"""Re-read any data needed to to synchronise with disk.
2189
1317
This method is intended to be called after another repository instance
2190
1318
(such as one used by a smart server) has inserted data into the
2191
repository. On all repositories this will work outside of write groups.
2192
Some repository formats (pack and newer for breezy native formats)
2193
support refresh_data inside write groups. If called inside a write
2194
group on a repository that does not support refreshing in a write group
2195
IsInWriteGroupError will be raised.
1319
repository. It may not be called during a write group, but may be
1320
called at any other time.
1322
if self.is_in_write_group():
1323
raise errors.InternalBzrError(
1324
"May not refresh_data while in a write group.")
2197
1325
if self._real_repository is not None:
2198
1326
self._real_repository.refresh_data()
2199
# Refresh the parents cache for this object
2200
self._unstacked_provider.disable_cache()
2201
self._unstacked_provider.enable_cache()
2203
1328
def revision_ids_to_search_result(self, result_set):
2204
1329
"""Convert a set of revision ids to a graph SearchResult."""
2205
1330
result_parents = set()
2206
for parents in viewvalues(self.get_graph().get_parent_map(result_set)):
1331
for parents in self.get_graph().get_parent_map(
1332
result_set).itervalues():
2207
1333
result_parents.update(parents)
2208
1334
included_keys = result_set.intersection(result_parents)
2209
1335
start_keys = result_set.difference(included_keys)
2210
1336
exclude_keys = result_parents.difference(result_set)
2211
result = vf_search.SearchResult(start_keys, exclude_keys,
2212
len(result_set), result_set)
1337
result = graph.SearchResult(start_keys, exclude_keys,
1338
len(result_set), result_set)
2215
def search_missing_revision_ids(self, other,
2216
find_ghosts=True, revision_ids=None, if_present_ids=None,
1342
def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
2218
1343
"""Return the revision ids that other has that this does not.
2220
1345
These are returned in topological order.
2222
1347
revision_id: only return revision ids included by revision_id.
2224
with self.lock_read():
2225
inter_repo = _mod_repository.InterRepository.get(other, self)
2226
return inter_repo.search_missing_revision_ids(
2227
find_ghosts=find_ghosts, revision_ids=revision_ids,
2228
if_present_ids=if_present_ids, limit=limit)
1349
return repository.InterRepository.get(
1350
other, self).search_missing_revision_ids(revision_id, find_ghosts)
2230
def fetch(self, source, revision_id=None, find_ghosts=False,
2231
fetch_spec=None, lossy=False):
1352
def fetch(self, source, revision_id=None, pb=None, find_ghosts=False,
2232
1354
# No base implementation to use as RemoteRepository is not a subclass
2233
1355
# of Repository; so this is a copy of Repository.fetch().
2234
1356
if fetch_spec is not None and revision_id is not None:
2272
1394
return self._real_repository._get_versioned_file_checker(
2273
1395
revisions, revision_versions_cache)
2275
def _iter_files_bytes_rpc(self, desired_files, absent):
2276
path = self.controldir._path_for_remote_call(self._client)
2279
for (file_id, revid, identifier) in desired_files:
2280
lines.append(b''.join([
2284
identifiers.append(identifier)
2285
(response_tuple, response_handler) = (
2286
self._call_with_body_bytes_expecting_body(
2287
b"Repository.iter_files_bytes", (path, ), b"\n".join(lines)))
2288
if response_tuple != (b'ok', ):
2289
response_handler.cancel_read_body()
2290
raise errors.UnexpectedSmartServerResponse(response_tuple)
2291
byte_stream = response_handler.read_streamed_body()
2293
def decompress_stream(start, byte_stream, unused):
2294
decompressor = zlib.decompressobj()
2295
yield decompressor.decompress(start)
2296
while decompressor.unused_data == b"":
2298
data = next(byte_stream)
2299
except StopIteration:
2301
yield decompressor.decompress(data)
2302
yield decompressor.flush()
2303
unused.append(decompressor.unused_data)
2306
while b"\n" not in unused:
2308
unused += next(byte_stream)
2309
except StopIteration:
2311
header, rest = unused.split(b"\n", 1)
2312
args = header.split(b"\0")
2313
if args[0] == b"absent":
2314
absent[identifiers[int(args[3])]] = (args[1], args[2])
2317
elif args[0] == b"ok":
2320
raise errors.UnexpectedSmartServerResponse(args)
2322
yield (identifiers[idx],
2323
decompress_stream(rest, byte_stream, unused_chunks))
2324
unused = b"".join(unused_chunks)
2326
1397
def iter_files_bytes(self, desired_files):
2327
1398
"""See Repository.iter_file_bytes.
2331
for (identifier, bytes_iterator) in self._iter_files_bytes_rpc(
2332
desired_files, absent):
2333
yield identifier, bytes_iterator
2334
for fallback in self._fallback_repositories:
2337
desired_files = [(key[0], key[1], identifier)
2338
for identifier, key in viewitems(absent)]
2339
for (identifier, bytes_iterator) in fallback.iter_files_bytes(desired_files):
2340
del absent[identifier]
2341
yield identifier, bytes_iterator
2343
# There may be more missing items, but raise an exception
2345
missing_identifier = next(iter(absent))
2346
missing_key = absent[missing_identifier]
2347
raise errors.RevisionNotPresent(revision_id=missing_key[1],
2348
file_id=missing_key[0])
2349
except errors.UnknownSmartMethod:
2351
for (identifier, bytes_iterator) in (
2352
self._real_repository.iter_files_bytes(desired_files)):
2353
yield identifier, bytes_iterator
2355
def get_cached_parent_map(self, revision_ids):
2356
"""See breezy.CachingParentsProvider.get_cached_parent_map"""
2357
return self._unstacked_provider.get_cached_parent_map(revision_ids)
1401
return self._real_repository.iter_files_bytes(desired_files)
2359
1403
def get_parent_map(self, revision_ids):
2360
"""See breezy.Graph.get_parent_map()."""
1404
"""See bzrlib.Graph.get_parent_map()."""
2361
1405
return self._make_parents_provider().get_parent_map(revision_ids)
2363
1407
def _get_parent_map_rpc(self, keys):
2480
1535
revision_graph[d[0]] = (NULL_REVISION,)
2481
1536
return revision_graph
2483
1539
def get_signature_text(self, revision_id):
2484
with self.lock_read():
2485
path = self.controldir._path_for_remote_call(self._client)
2487
response_tuple, response_handler = self._call_expecting_body(
2488
b'Repository.get_revision_signature_text', path, revision_id)
2489
except errors.UnknownSmartMethod:
2491
return self._real_repository.get_signature_text(revision_id)
2492
except errors.NoSuchRevision as err:
2493
for fallback in self._fallback_repositories:
2495
return fallback.get_signature_text(revision_id)
2496
except errors.NoSuchRevision:
2500
if response_tuple[0] != b'ok':
2501
raise errors.UnexpectedSmartServerResponse(response_tuple)
2502
return response_handler.read_body_bytes()
1541
return self._real_repository.get_signature_text(revision_id)
2504
1544
def _get_inventory_xml(self, revision_id):
2505
with self.lock_read():
2506
# This call is used by older working tree formats,
2507
# which stored a serialized basis inventory.
2509
return self._real_repository._get_inventory_xml(revision_id)
1546
return self._real_repository._get_inventory_xml(revision_id)
2511
1548
def reconcile(self, other=None, thorough=False):
2512
from ..reconcile import ReconcileResult
2513
with self.lock_write():
2514
path = self.controldir._path_for_remote_call(self._client)
2516
response, handler = self._call_expecting_body(
2517
b'Repository.reconcile', path, self._lock_token)
2518
except (errors.UnknownSmartMethod, errors.TokenLockingNotSupported):
2520
return self._real_repository.reconcile(other=other, thorough=thorough)
2521
if response != (b'ok', ):
2522
raise errors.UnexpectedSmartServerResponse(response)
2523
body = handler.read_body_bytes()
2524
result = ReconcileResult()
2525
result.garbage_inventories = None
2526
result.inconsistent_parents = None
2527
result.aborted = None
2528
for line in body.split(b'\n'):
2531
key, val_text = line.split(b':')
2532
if key == b"garbage_inventories":
2533
result.garbage_inventories = int(val_text)
2534
elif key == b"inconsistent_parents":
2535
result.inconsistent_parents = int(val_text)
2537
mutter("unknown reconcile key %r" % key)
1550
return self._real_repository.reconcile(other=other, thorough=thorough)
2540
1552
def all_revision_ids(self):
2541
path = self.controldir._path_for_remote_call(self._client)
2543
response_tuple, response_handler = self._call_expecting_body(
2544
b"Repository.all_revision_ids", path)
2545
except errors.UnknownSmartMethod:
2547
return self._real_repository.all_revision_ids()
2548
if response_tuple != (b"ok", ):
2549
raise errors.UnexpectedSmartServerResponse(response_tuple)
2550
revids = set(response_handler.read_body_bytes().splitlines())
2551
for fallback in self._fallback_repositories:
2552
revids.update(set(fallback.all_revision_ids()))
2555
def _filtered_revision_trees(self, revision_ids, file_ids):
2556
"""Return Tree for a revision on this branch with only some files.
2558
:param revision_ids: a sequence of revision-ids;
2559
a revision-id may not be None or b'null:'
2560
:param file_ids: if not None, the result is filtered
2561
so that only those file-ids, their parents and their
2562
children are included.
2564
inventories = self.iter_inventories(revision_ids)
2565
for inv in inventories:
2566
# Should we introduce a FilteredRevisionTree class rather
2567
# than pre-filter the inventory here?
2568
filtered_inv = inv.filter(file_ids)
2569
yield InventoryRevisionTree(self, filtered_inv, filtered_inv.revision_id)
2571
def get_revision_delta(self, revision_id):
2572
with self.lock_read():
2573
r = self.get_revision(revision_id)
2574
return list(self.get_revision_deltas([r]))[0]
1554
return self._real_repository.all_revision_ids()
1557
def get_deltas_for_revisions(self, revisions, specific_fileids=None):
1559
return self._real_repository.get_deltas_for_revisions(revisions,
1560
specific_fileids=specific_fileids)
1563
def get_revision_delta(self, revision_id, specific_fileids=None):
1565
return self._real_repository.get_revision_delta(revision_id,
1566
specific_fileids=specific_fileids)
2576
1569
def revision_trees(self, revision_ids):
2577
with self.lock_read():
2578
inventories = self.iter_inventories(revision_ids)
2579
for inv in inventories:
2580
yield RemoteInventoryTree(self, inv, inv.revision_id)
1571
return self._real_repository.revision_trees(revision_ids)
2582
1574
def get_revision_reconcile(self, revision_id):
2583
with self.lock_read():
2585
return self._real_repository.get_revision_reconcile(revision_id)
1576
return self._real_repository.get_revision_reconcile(revision_id)
2587
1579
def check(self, revision_ids=None, callback_refs=None, check_repo=True):
2588
with self.lock_read():
2590
return self._real_repository.check(revision_ids=revision_ids,
2591
callback_refs=callback_refs, check_repo=check_repo)
1581
return self._real_repository.check(revision_ids=revision_ids,
1582
callback_refs=callback_refs, check_repo=check_repo)
2593
1584
def copy_content_into(self, destination, revision_id=None):
2594
"""Make a complete copy of the content in self into destination.
2596
This is a destructive operation! Do not use it on existing
2599
interrepo = _mod_repository.InterRepository.get(self, destination)
2600
return interrepo.copy_content(revision_id)
1586
return self._real_repository.copy_content_into(
1587
destination, revision_id=revision_id)
2602
1589
def _copy_repository_tarball(self, to_bzrdir, revision_id=None):
2603
1590
# get a tarball of the remote repository, and copy from that into the
1592
from bzrlib import osutils
2606
1594
# TODO: Maybe a progress bar while streaming the tarball?
2607
note(gettext("Copying repository content as tarball..."))
1595
note("Copying repository content as tarball...")
2608
1596
tar_file = self._get_tarball('bz2')
2609
1597
if tar_file is None:
2611
1599
destination = to_bzrdir.create_repository()
2613
1601
tar = tarfile.open('repository', fileobj=tar_file,
2615
1603
tmpdir = osutils.mkdtemp()
2617
tar.extractall(tmpdir)
2618
tmp_bzrdir = _mod_bzrdir.BzrDir.open(tmpdir)
1605
_extract_tar(tar, tmpdir)
1606
tmp_bzrdir = BzrDir.open(tmpdir)
2619
1607
tmp_repo = tmp_bzrdir.open_repository()
2620
1608
tmp_repo.copy_content_into(destination, revision_id)
2709
1686
self._ensure_real()
2710
1687
return self._real_repository.texts
2712
def _iter_revisions_rpc(self, revision_ids):
2713
body = b"\n".join(revision_ids)
2714
path = self.controldir._path_for_remote_call(self._client)
2715
response_tuple, response_handler = (
2716
self._call_with_body_bytes_expecting_body(
2717
b"Repository.iter_revisions", (path, ), body))
2718
if response_tuple[0] != b"ok":
2719
raise errors.UnexpectedSmartServerResponse(response_tuple)
2720
serializer_format = response_tuple[1].decode('ascii')
2721
serializer = serializer_format_registry.get(serializer_format)
2722
byte_stream = response_handler.read_streamed_body()
2723
decompressor = zlib.decompressobj()
2725
for bytes in byte_stream:
2726
chunks.append(decompressor.decompress(bytes))
2727
if decompressor.unused_data != b"":
2728
chunks.append(decompressor.flush())
2729
yield serializer.read_revision_from_string(b"".join(chunks))
2730
unused = decompressor.unused_data
2731
decompressor = zlib.decompressobj()
2732
chunks = [decompressor.decompress(unused)]
2733
chunks.append(decompressor.flush())
2734
text = b"".join(chunks)
2736
yield serializer.read_revision_from_string(b"".join(chunks))
2738
def iter_revisions(self, revision_ids):
2739
for rev_id in revision_ids:
2740
if not rev_id or not isinstance(rev_id, bytes):
2741
raise errors.InvalidRevisionId(
2742
revision_id=rev_id, branch=self)
2743
with self.lock_read():
2745
missing = set(revision_ids)
2746
for rev in self._iter_revisions_rpc(revision_ids):
2747
missing.remove(rev.revision_id)
2748
yield (rev.revision_id, rev)
2749
for fallback in self._fallback_repositories:
2752
for (revid, rev) in fallback.iter_revisions(missing):
2755
missing.remove(revid)
2756
for revid in missing:
2758
except errors.UnknownSmartMethod:
2760
for entry in self._real_repository.iter_revisions(revision_ids):
1690
def get_revisions(self, revision_ids):
1692
return self._real_repository.get_revisions(revision_ids)
2763
1694
def supports_rich_root(self):
2764
1695
return self._format.rich_root_data
1697
def iter_reverse_revision_history(self, revision_id):
1699
return self._real_repository.iter_reverse_revision_history(revision_id)
2767
1702
def _serializer(self):
2768
1703
return self._format._serializer
2770
1705
def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
2771
with self.lock_write():
2772
signature = gpg_strategy.sign(plaintext, gpg.MODE_CLEAR)
2773
self.add_signature_text(revision_id, signature)
1707
return self._real_repository.store_revision_signature(
1708
gpg_strategy, plaintext, revision_id)
2775
1710
def add_signature_text(self, revision_id, signature):
2776
if self._real_repository:
2777
# If there is a real repository the write group will
2778
# be in the real repository as well, so use that:
2780
return self._real_repository.add_signature_text(
2781
revision_id, signature)
2782
path = self.controldir._path_for_remote_call(self._client)
2783
response, handler = self._call_with_body_bytes_expecting_body(
2784
b'Repository.add_signature_text', (path, self._lock_token,
2786
tuple([token.encode('utf-8')
2787
for token in self._write_group_tokens]),
2789
handler.cancel_read_body()
2791
if response[0] != b'ok':
2792
raise errors.UnexpectedSmartServerResponse(response)
2793
self._write_group_tokens = [token.decode(
2794
'utf-8') for token in response[1:]]
1712
return self._real_repository.add_signature_text(revision_id, signature)
2796
1714
def has_signature_for_revision_id(self, revision_id):
2797
path = self.controldir._path_for_remote_call(self._client)
2799
response = self._call(b'Repository.has_signature_for_revision_id',
2801
except errors.UnknownSmartMethod:
2803
return self._real_repository.has_signature_for_revision_id(
2805
if response[0] not in (b'yes', b'no'):
2806
raise SmartProtocolError(
2807
'unexpected response code %s' % (response,))
2808
if response[0] == b'yes':
2810
for fallback in self._fallback_repositories:
2811
if fallback.has_signature_for_revision_id(revision_id):
2815
def verify_revision_signature(self, revision_id, gpg_strategy):
2816
with self.lock_read():
2817
if not self.has_signature_for_revision_id(revision_id):
2818
return gpg.SIGNATURE_NOT_SIGNED, None
2819
signature = self.get_signature_text(revision_id)
2821
testament = _mod_testament.Testament.from_revision(
2824
(status, key, signed_plaintext) = gpg_strategy.verify(signature)
2825
if testament.as_short_text() != signed_plaintext:
2826
return gpg.SIGNATURE_NOT_VALID, None
2827
return (status, key)
1716
return self._real_repository.has_signature_for_revision_id(revision_id)
2829
1718
def item_keys_introduced_by(self, revision_ids, _files_pb=None):
2830
1719
self._ensure_real()
2831
1720
return self._real_repository.item_keys_introduced_by(revision_ids,
2832
_files_pb=_files_pb)
1721
_files_pb=_files_pb)
1723
def revision_graph_can_have_wrong_parents(self):
1724
# The answer depends on the remote repo format.
1726
return self._real_repository.revision_graph_can_have_wrong_parents()
2834
1728
def _find_inconsistent_revision_parents(self, revisions_iterator=None):
2835
1729
self._ensure_real()
2853
1748
:param recipe: A search recipe (start, stop, count).
2854
1749
:return: Serialised bytes.
2856
start_keys = b' '.join(recipe[1])
2857
stop_keys = b' '.join(recipe[2])
2858
count = str(recipe[3]).encode('ascii')
2859
return b'\n'.join((start_keys, stop_keys, count))
1751
start_keys = ' '.join(recipe[1])
1752
stop_keys = ' '.join(recipe[2])
1753
count = str(recipe[3])
1754
return '\n'.join((start_keys, stop_keys, count))
2861
1756
def _serialise_search_result(self, search_result):
2862
parts = search_result.get_network_struct()
2863
return b'\n'.join(parts)
1757
if isinstance(search_result, graph.PendingAncestryResult):
1758
parts = ['ancestry-of']
1759
parts.extend(search_result.heads)
1761
recipe = search_result.get_recipe()
1762
parts = [recipe[0], self._serialise_search_recipe(recipe)]
1763
return '\n'.join(parts)
2865
1765
def autopack(self):
2866
path = self.controldir._path_for_remote_call(self._client)
1766
path = self.bzrdir._path_for_remote_call(self._client)
2868
response = self._call(b'PackRepository.autopack', path)
1768
response = self._call('PackRepository.autopack', path)
2869
1769
except errors.UnknownSmartMethod:
2870
1770
self._ensure_real()
2871
1771
self._real_repository._pack_collection.autopack()
2873
1773
self.refresh_data()
2874
if response[0] != b'ok':
2875
raise errors.UnexpectedSmartServerResponse(response)
2877
def _revision_archive(self, revision_id, format, name, root, subdir,
2879
path = self.controldir._path_for_remote_call(self._client)
2880
format = format or ''
2882
subdir = subdir or ''
2883
force_mtime = int(force_mtime) if force_mtime is not None else None
2885
response, protocol = self._call_expecting_body(
2886
b'Repository.revision_archive', path,
2888
format.encode('ascii'),
2889
os.path.basename(name).encode('utf-8'),
2890
root.encode('utf-8'),
2891
subdir.encode('utf-8'),
2893
except errors.UnknownSmartMethod:
2895
if response[0] == b'ok':
2896
return iter([protocol.read_body_bytes()])
2897
raise errors.UnexpectedSmartServerResponse(response)
2899
def _annotate_file_revision(self, revid, tree_path, file_id, default_revision):
2900
path = self.controldir._path_for_remote_call(self._client)
2901
tree_path = tree_path.encode('utf-8')
2902
file_id = file_id or b''
2903
default_revision = default_revision or b''
2905
response, handler = self._call_expecting_body(
2906
b'Repository.annotate_file_revision', path,
2907
revid, tree_path, file_id, default_revision)
2908
except errors.UnknownSmartMethod:
2910
if response[0] != b'ok':
2911
raise errors.UnexpectedSmartServerResponse(response)
2912
return map(tuple, bencode.bdecode(handler.read_body_bytes()))
2915
class RemoteStreamSink(vf_repository.StreamSink):
1774
if response[0] != 'ok':
1775
raise errors.UnexpectedSmartServerResponse(response)
1778
class RemoteStreamSink(repository.StreamSink):
2917
1780
def _insert_real(self, stream, src_format, resume_tokens):
2918
1781
self.target_repo._ensure_real()
3262
2065
def network_name(self):
3263
2066
return self._network_name
3265
def open(self, a_controldir, name=None, ignore_fallbacks=False):
3266
return a_controldir.open_branch(name=name,
3267
ignore_fallbacks=ignore_fallbacks)
2068
def open(self, a_bzrdir, name=None, ignore_fallbacks=False):
2069
return a_bzrdir.open_branch(name=name,
2070
ignore_fallbacks=ignore_fallbacks)
3269
def _vfs_initialize(self, a_controldir, name, append_revisions_only,
2072
def _vfs_initialize(self, a_bzrdir, name):
3271
2073
# Initialisation when using a local bzrdir object, or a non-vfs init
3272
2074
# method is not available on the server.
3273
2075
# self._custom_format is always set - the start of initialize ensures
3275
if isinstance(a_controldir, RemoteBzrDir):
3276
a_controldir._ensure_real()
3277
result = self._custom_format.initialize(a_controldir._real_bzrdir,
3278
name=name, append_revisions_only=append_revisions_only,
3279
repository=repository)
2077
if isinstance(a_bzrdir, RemoteBzrDir):
2078
a_bzrdir._ensure_real()
2079
result = self._custom_format.initialize(a_bzrdir._real_bzrdir,
3281
2082
# We assume the bzrdir is parameterised; it may not be.
3282
result = self._custom_format.initialize(a_controldir, name=name,
3283
append_revisions_only=append_revisions_only,
3284
repository=repository)
3285
if (isinstance(a_controldir, RemoteBzrDir)
3286
and not isinstance(result, RemoteBranch)):
3287
result = RemoteBranch(a_controldir, a_controldir.find_repository(), result,
2083
result = self._custom_format.initialize(a_bzrdir, name)
2084
if (isinstance(a_bzrdir, RemoteBzrDir) and
2085
not isinstance(result, RemoteBranch)):
2086
result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result,
3291
def initialize(self, a_controldir, name=None, repository=None,
3292
append_revisions_only=None):
3294
name = a_controldir._get_selected_branch()
2090
def initialize(self, a_bzrdir, name=None):
3295
2091
# 1) get the network name to use.
3296
2092
if self._custom_format:
3297
2093
network_name = self._custom_format.network_name()
3299
# Select the current breezy default and ask for that.
3300
reference_bzrdir_format = controldir.format_registry.get(
2095
# Select the current bzrlib default and ask for that.
2096
reference_bzrdir_format = bzrdir.format_registry.get('default')()
3302
2097
reference_format = reference_bzrdir_format.get_branch_format()
3303
2098
self._custom_format = reference_format
3304
2099
network_name = reference_format.network_name()
3305
2100
# Being asked to create on a non RemoteBzrDir:
3306
if not isinstance(a_controldir, RemoteBzrDir):
3307
return self._vfs_initialize(a_controldir, name=name,
3308
append_revisions_only=append_revisions_only,
3309
repository=repository)
3310
medium = a_controldir._client._medium
2101
if not isinstance(a_bzrdir, RemoteBzrDir):
2102
return self._vfs_initialize(a_bzrdir, name=name)
2103
medium = a_bzrdir._client._medium
3311
2104
if medium._is_remote_before((1, 13)):
3312
return self._vfs_initialize(a_controldir, name=name,
3313
append_revisions_only=append_revisions_only,
3314
repository=repository)
2105
return self._vfs_initialize(a_bzrdir, name=name)
3315
2106
# Creating on a remote bzr dir.
3316
2107
# 2) try direct creation via RPC
3317
path = a_controldir._path_for_remote_call(a_controldir._client)
2108
path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
2109
if name is not None:
3319
2110
# XXX JRV20100304: Support creating colocated branches
3320
2111
raise errors.NoColocatedBranchSupport(self)
3321
verb = b'BzrDir.create_branch'
2112
verb = 'BzrDir.create_branch'
3323
response = a_controldir._call(verb, path, network_name)
2114
response = a_bzrdir._call(verb, path, network_name)
3324
2115
except errors.UnknownSmartMethod:
3325
2116
# Fallback - use vfs methods
3326
2117
medium._remember_remote_is_before((1, 13))
3327
return self._vfs_initialize(a_controldir, name=name,
3328
append_revisions_only=append_revisions_only,
3329
repository=repository)
3330
if response[0] != b'ok':
2118
return self._vfs_initialize(a_bzrdir, name=name)
2119
if response[0] != 'ok':
3331
2120
raise errors.UnexpectedSmartServerResponse(response)
3332
2121
# Turn the response into a RemoteRepository object.
3333
2122
format = RemoteBranchFormat(network_name=response[1])
3334
2123
repo_format = response_tuple_to_repo_format(response[3:])
3335
repo_path = response[2].decode('utf-8')
3336
if repository is not None:
3337
remote_repo_url = urlutils.join(a_controldir.user_url, repo_path)
3338
url_diff = urlutils.relative_url(repository.user_url,
3341
raise AssertionError(
3342
'repository.user_url %r does not match URL from server '
3343
'response (%r + %r)'
3344
% (repository.user_url, a_controldir.user_url, repo_path))
3345
remote_repo = repository
2124
if response[2] == '':
2125
repo_bzrdir = a_bzrdir
3348
repo_bzrdir = a_controldir
3350
repo_bzrdir = RemoteBzrDir(
3351
a_controldir.root_transport.clone(
3352
repo_path), a_controldir._format,
3353
a_controldir._client)
3354
remote_repo = RemoteRepository(repo_bzrdir, repo_format)
3355
remote_branch = RemoteBranch(a_controldir, remote_repo,
3356
format=format, setup_stacking=False, name=name)
3357
if append_revisions_only:
3358
remote_branch.set_append_revisions_only(append_revisions_only)
2127
repo_bzrdir = RemoteBzrDir(
2128
a_bzrdir.root_transport.clone(response[2]), a_bzrdir._format,
2130
remote_repo = RemoteRepository(repo_bzrdir, repo_format)
2131
remote_branch = RemoteBranch(a_bzrdir, remote_repo,
2132
format=format, setup_stacking=False, name=name)
3359
2133
# XXX: We know this is a new branch, so it must have revno 0, revid
3360
2134
# NULL_REVISION. Creating the branch locked would make this be unable
3361
2135
# to be wrong; here its simply very unlikely to be wrong. RBC 20090225
3380
2154
self._ensure_real()
3381
2155
return self._custom_format.supports_set_append_revisions_only()
3384
def supports_reference_locations(self):
3386
return self._custom_format.supports_reference_locations
3388
def stores_revno(self):
3391
def _use_default_local_heads_to_fetch(self):
3392
# If the branch format is a metadir format *and* its heads_to_fetch
3393
# implementation is not overridden vs the base class, we can use the
3394
# base class logic rather than use the heads_to_fetch RPC. This is
3395
# usually cheaper in terms of net round trips, as the last-revision and
3396
# tags info fetched is cached and would be fetched anyway.
3398
if isinstance(self._custom_format, bzrbranch.BranchFormatMetadir):
3399
branch_class = self._custom_format._branch_class()
3400
heads_to_fetch_impl = get_unbound_function(
3401
branch_class.heads_to_fetch)
3402
if heads_to_fetch_impl is get_unbound_function(branch.Branch.heads_to_fetch):
3407
class RemoteBranchStore(_mod_config.IniFileStore):
3408
"""Branch store which attempts to use HPSS calls to retrieve branch store.
3410
Note that this is specific to bzr-based formats.
3413
def __init__(self, branch):
3414
super(RemoteBranchStore, self).__init__()
3415
self.branch = branch
3417
self._real_store = None
3419
def external_url(self):
3420
return urlutils.join(self.branch.user_url, 'branch.conf')
3422
def _load_content(self):
3423
path = self.branch._remote_path()
3425
response, handler = self.branch._call_expecting_body(
3426
b'Branch.get_config_file', path)
3427
except errors.UnknownSmartMethod:
3429
return self._real_store._load_content()
3430
if len(response) and response[0] != b'ok':
3431
raise errors.UnexpectedSmartServerResponse(response)
3432
return handler.read_body_bytes()
3434
def _save_content(self, content):
3435
path = self.branch._remote_path()
3437
response, handler = self.branch._call_with_body_bytes_expecting_body(
3438
b'Branch.put_config_file', (path,
3439
self.branch._lock_token, self.branch._repo_lock_token),
3441
except errors.UnknownSmartMethod:
3443
return self._real_store._save_content(content)
3444
handler.cancel_read_body()
3445
if response != (b'ok', ):
3446
raise errors.UnexpectedSmartServerResponse(response)
3448
def _ensure_real(self):
3449
self.branch._ensure_real()
3450
if self._real_store is None:
3451
self._real_store = _mod_config.BranchStore(self.branch)
3454
2158
class RemoteBranch(branch.Branch, _RpcHelper, lock._RelockDebugMixin):
3455
2159
"""Branch stored on a server accessed by HPSS RPC.
4006
2655
self._ensure_real()
4007
2656
return self._real_branch._set_parent_location(url)
4009
2659
def pull(self, source, overwrite=False, stop_revision=None,
4011
with self.lock_write():
4012
self._clear_cached_state_of_remote_branch_only()
4014
return self._real_branch.pull(
4015
source, overwrite=overwrite, stop_revision=stop_revision,
4016
_override_hook_target=self, **kwargs)
4018
def push(self, target, overwrite=False, stop_revision=None, lossy=False, tag_selector=None):
4019
with self.lock_read():
4021
return self._real_branch.push(
4022
target, overwrite=overwrite, stop_revision=stop_revision, lossy=lossy,
4023
_override_hook_source_branch=self, tag_selector=tag_selector)
4025
def peek_lock_mode(self):
4026
return self._lock_mode
2661
self._clear_cached_state_of_remote_branch_only()
2663
return self._real_branch.pull(
2664
source, overwrite=overwrite, stop_revision=stop_revision,
2665
_override_hook_target=self, **kwargs)
2668
def push(self, target, overwrite=False, stop_revision=None):
2670
return self._real_branch.push(
2671
target, overwrite=overwrite, stop_revision=stop_revision,
2672
_override_hook_source_branch=self)
4028
2674
def is_locked(self):
4029
2675
return self._lock_count >= 1
4031
def revision_id_to_dotted_revno(self, revision_id):
4032
"""Given a revision id, return its dotted revno.
4034
:return: a tuple like (1,) or (400,1,3).
4036
with self.lock_read():
4038
response = self._call(b'Branch.revision_id_to_revno',
4039
self._remote_path(), revision_id)
4040
except errors.UnknownSmartMethod:
4042
return self._real_branch.revision_id_to_dotted_revno(revision_id)
4043
except errors.UnknownErrorFromSmartServer as e:
4044
# Deal with older versions of bzr/brz that didn't explicitly
4045
# wrap GhostRevisionsHaveNoRevno.
4046
if e.error_tuple[1] == b'GhostRevisionsHaveNoRevno':
4047
(revid, ghost_revid) = re.findall(b"{([^}]+)}", e.error_tuple[2])
4048
raise errors.GhostRevisionsHaveNoRevno(
4051
if response[0] == b'ok':
4052
return tuple([int(x) for x in response[1:]])
4054
raise errors.UnexpectedSmartServerResponse(response)
4056
2678
def revision_id_to_revno(self, revision_id):
4057
"""Given a revision id on the branch mainline, return its revno.
4061
with self.lock_read():
4063
response = self._call(b'Branch.revision_id_to_revno',
4064
self._remote_path(), revision_id)
4065
except errors.UnknownSmartMethod:
4067
return self._real_branch.revision_id_to_revno(revision_id)
4068
if response[0] == b'ok':
4069
if len(response) == 2:
4070
return int(response[1])
4071
raise NoSuchRevision(self, revision_id)
4073
raise errors.UnexpectedSmartServerResponse(response)
2680
return self._real_branch.revision_id_to_revno(revision_id)
4075
2683
def set_last_revision_info(self, revno, revision_id):
4076
with self.lock_write():
4077
# XXX: These should be returned by the set_last_revision_info verb
4078
old_revno, old_revid = self.last_revision_info()
4079
self._run_pre_change_branch_tip_hooks(revno, revision_id)
4080
if not revision_id or not isinstance(revision_id, bytes):
4081
raise errors.InvalidRevisionId(
4082
revision_id=revision_id, branch=self)
4084
response = self._call(b'Branch.set_last_revision_info',
4085
self._remote_path(), self._lock_token, self._repo_lock_token,
4086
str(revno).encode('ascii'), revision_id)
4087
except errors.UnknownSmartMethod:
4089
self._clear_cached_state_of_remote_branch_only()
4090
self._real_branch.set_last_revision_info(revno, revision_id)
4091
self._last_revision_info_cache = revno, revision_id
4093
if response == (b'ok',):
4094
self._clear_cached_state()
4095
self._last_revision_info_cache = revno, revision_id
4096
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
4097
# Update the _real_branch's cache too.
4098
if self._real_branch is not None:
4099
cache = self._last_revision_info_cache
4100
self._real_branch._last_revision_info_cache = cache
4102
raise errors.UnexpectedSmartServerResponse(response)
2684
# XXX: These should be returned by the set_last_revision_info verb
2685
old_revno, old_revid = self.last_revision_info()
2686
self._run_pre_change_branch_tip_hooks(revno, revision_id)
2687
revision_id = ensure_null(revision_id)
2689
response = self._call('Branch.set_last_revision_info',
2690
self._remote_path(), self._lock_token, self._repo_lock_token,
2691
str(revno), revision_id)
2692
except errors.UnknownSmartMethod:
2694
self._clear_cached_state_of_remote_branch_only()
2695
self._real_branch.set_last_revision_info(revno, revision_id)
2696
self._last_revision_info_cache = revno, revision_id
2698
if response == ('ok',):
2699
self._clear_cached_state()
2700
self._last_revision_info_cache = revno, revision_id
2701
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2702
# Update the _real_branch's cache too.
2703
if self._real_branch is not None:
2704
cache = self._last_revision_info_cache
2705
self._real_branch._last_revision_info_cache = cache
2707
raise errors.UnexpectedSmartServerResponse(response)
4104
2710
def generate_revision_history(self, revision_id, last_rev=None,
4105
2711
other_branch=None):
4106
with self.lock_write():
4107
medium = self._client._medium
4108
if not medium._is_remote_before((1, 6)):
4109
# Use a smart method for 1.6 and above servers
4111
self._set_last_revision_descendant(revision_id, other_branch,
4112
allow_diverged=True, allow_overwrite_descendant=True)
4114
except errors.UnknownSmartMethod:
4115
medium._remember_remote_is_before((1, 6))
4116
self._clear_cached_state_of_remote_branch_only()
4117
graph = self.repository.get_graph()
4118
(last_revno, last_revid) = self.last_revision_info()
4119
known_revision_ids = [
4120
(last_revid, last_revno),
4121
(_mod_revision.NULL_REVISION, 0),
4123
if last_rev is not None:
4124
if not graph.is_ancestor(last_rev, revision_id):
4125
# our previous tip is not merged into stop_revision
4126
raise errors.DivergedBranches(self, other_branch)
4127
revno = graph.find_distance_to_null(
4128
revision_id, known_revision_ids)
4129
self.set_last_revision_info(revno, revision_id)
2712
medium = self._client._medium
2713
if not medium._is_remote_before((1, 6)):
2714
# Use a smart method for 1.6 and above servers
2716
self._set_last_revision_descendant(revision_id, other_branch,
2717
allow_diverged=True, allow_overwrite_descendant=True)
2719
except errors.UnknownSmartMethod:
2720
medium._remember_remote_is_before((1, 6))
2721
self._clear_cached_state_of_remote_branch_only()
2722
self.set_revision_history(self._lefthand_history(revision_id,
2723
last_rev=last_rev,other_branch=other_branch))
4131
2725
def set_push_location(self, location):
4132
self._set_config_location('push_location', location)
4134
def heads_to_fetch(self):
4135
if self._format._use_default_local_heads_to_fetch():
4136
# We recognise this format, and its heads-to-fetch implementation
4137
# is the default one (tip + tags). In this case it's cheaper to
4138
# just use the default implementation rather than a special RPC as
4139
# the tip and tags data is cached.
4140
return branch.Branch.heads_to_fetch(self)
4141
medium = self._client._medium
4142
if medium._is_remote_before((2, 4)):
4143
return self._vfs_heads_to_fetch()
4145
return self._rpc_heads_to_fetch()
4146
except errors.UnknownSmartMethod:
4147
medium._remember_remote_is_before((2, 4))
4148
return self._vfs_heads_to_fetch()
4150
def _rpc_heads_to_fetch(self):
4151
response = self._call(b'Branch.heads_to_fetch', self._remote_path())
4152
if len(response) != 2:
4153
raise errors.UnexpectedSmartServerResponse(response)
4154
must_fetch, if_present_fetch = response
4155
return set(must_fetch), set(if_present_fetch)
4157
def _vfs_heads_to_fetch(self):
4159
return self._real_branch.heads_to_fetch()
4161
def reconcile(self, thorough=True):
4162
"""Make sure the data stored in this branch is consistent."""
4163
from .reconcile import BranchReconciler
4164
with self.lock_write():
4165
reconciler = BranchReconciler(self, thorough=thorough)
4166
return reconciler.reconcile()
4168
def get_reference_info(self, file_id):
4169
"""Get the tree_path and branch_location for a tree reference."""
4170
if not self._format.supports_reference_locations:
4171
raise errors.UnsupportedOperation(self.get_reference_info, self)
4172
return self._get_all_reference_info().get(file_id, (None, None))
4174
def set_reference_info(self, file_id, branch_location, tree_path=None):
4175
"""Set the branch location to use for a tree reference."""
4176
if not self._format.supports_reference_locations:
4177
raise errors.UnsupportedOperation(self.set_reference_info, self)
4179
self._real_branch.set_reference_info(
4180
file_id, branch_location, tree_path)
4182
def _set_all_reference_info(self, reference_info):
4183
if not self._format.supports_reference_locations:
4184
raise errors.UnsupportedOperation(self.set_reference_info, self)
4186
self._real_branch._set_all_reference_info(reference_info)
4188
def _get_all_reference_info(self):
4189
if not self._format.supports_reference_locations:
4192
response, handler = self._call_expecting_body(
4193
b'Branch.get_all_reference_info', self._remote_path())
4194
except errors.UnknownSmartMethod:
4196
return self._real_branch._get_all_reference_info()
4197
if len(response) and response[0] != b'ok':
4198
raise errors.UnexpectedSmartServerResponse(response)
4200
for (f, u, p) in bencode.bdecode(handler.read_body_bytes()):
4201
ret[f] = (u.decode('utf-8'), p.decode('utf-8') if p else None)
4204
def reference_parent(self, file_id, path, possible_transports=None):
4205
"""Return the parent branch for a tree-reference.
4207
:param path: The path of the nested tree in the tree
4208
:return: A branch associated with the nested tree
4210
branch_location = self.get_reference_info(file_id)[0]
4211
if branch_location is None:
4213
return branch.Branch.open_from_transport(
4214
self.controldir.root_transport.clone(path),
4215
possible_transports=possible_transports)
4216
except errors.NotBranchError:
4218
return branch.Branch.open(
4220
urlutils.strip_segment_parameters(self.user_url), branch_location),
4221
possible_transports=possible_transports)
2727
return self._real_branch.set_push_location(location)
4224
2730
class RemoteConfig(object):
4289
2785
medium = self._branch._client._medium
4290
2786
if medium._is_remote_before((1, 14)):
4291
2787
return self._vfs_set_option(value, name, section)
4292
if isinstance(value, dict):
4293
if medium._is_remote_before((2, 2)):
4294
return self._vfs_set_option(value, name, section)
4295
return self._set_config_option_dict(value, name, section)
4297
return self._set_config_option(value, name, section)
4299
def _set_config_option(self, value, name, section):
4300
if isinstance(value, (bool, int)):
4302
elif isinstance(value, (text_type, str)):
4305
raise TypeError(value)
4307
2789
path = self._branch._remote_path()
4308
response = self._branch._client.call(b'Branch.set_config_option',
4309
path, self._branch._lock_token, self._branch._repo_lock_token,
4310
value.encode('utf-8'), name.encode('utf-8'),
4311
(section or '').encode('utf-8'))
2790
response = self._branch._client.call('Branch.set_config_option',
2791
path, self._branch._lock_token, self._branch._repo_lock_token,
2792
value.encode('utf8'), name, section or '')
4312
2793
except errors.UnknownSmartMethod:
4313
medium = self._branch._client._medium
4314
2794
medium._remember_remote_is_before((1, 14))
4315
2795
return self._vfs_set_option(value, name, section)
4316
2796
if response != ():
4317
2797
raise errors.UnexpectedSmartServerResponse(response)
4319
def _serialize_option_dict(self, option_dict):
4321
for key, value in option_dict.items():
4322
if isinstance(key, text_type):
4323
key = key.encode('utf8')
4324
if isinstance(value, text_type):
4325
value = value.encode('utf8')
4326
utf8_dict[key] = value
4327
return bencode.bencode(utf8_dict)
4329
def _set_config_option_dict(self, value, name, section):
4331
path = self._branch._remote_path()
4332
serialised_dict = self._serialize_option_dict(value)
4333
response = self._branch._client.call(
4334
b'Branch.set_config_option_dict',
4335
path, self._branch._lock_token, self._branch._repo_lock_token,
4336
serialised_dict, name.encode('utf-8'), (section or '').encode('utf-8'))
4337
except errors.UnknownSmartMethod:
4338
medium = self._branch._client._medium
4339
medium._remember_remote_is_before((2, 2))
4340
return self._vfs_set_option(value, name, section)
4342
raise errors.UnexpectedSmartServerResponse(response)
4344
2799
def _real_object(self):
4345
2800
self._branch._ensure_real()
4346
2801
return self._branch._real_branch
4406
2867
def find(name):
4408
2869
return context[name]
4410
mutter('Missing key \'%s\' in context %r', name, context)
2870
except KeyError, key_err:
2871
mutter('Missing key %r in context %r', key_err.args[0], context)
4413
2873
def get_path():
4414
2874
"""Get the path from the context if present, otherwise use first error
4418
2878
return context['path']
2879
except KeyError, key_err:
4421
return err.error_args[0].decode('utf-8')
4423
mutter('Missing key \'path\' in context %r', context)
2881
return err.error_args[0]
2882
except IndexError, idx_err:
2884
'Missing key %r in context %r', key_err.args[0], context)
4425
if not isinstance(err.error_verb, bytes):
4426
raise TypeError(err.error_verb)
4428
translator = error_translators.get(err.error_verb)
4432
raise translator(err, find, get_path)
4434
translator = no_context_error_translators.get(err.error_verb)
4436
raise errors.UnknownErrorFromSmartServer(err)
4438
raise translator(err)
4441
error_translators.register(b'NoSuchRevision',
4442
lambda err, find, get_path: NoSuchRevision(
4443
find('branch'), err.error_args[0]))
4444
error_translators.register(b'nosuchrevision',
4445
lambda err, find, get_path: NoSuchRevision(
4446
find('repository'), err.error_args[0]))
4447
error_translators.register(
4448
b'revno-outofbounds',
4449
lambda err, find, get_path: errors.RevnoOutOfBounds(
4450
err.error_args[0], (err.error_args[1], err.error_args[2])))
4453
def _translate_nobranch_error(err, find, get_path):
4454
if len(err.error_args) >= 1:
4455
extra = err.error_args[0].decode('utf-8')
4458
return errors.NotBranchError(path=find('bzrdir').root_transport.base,
4462
error_translators.register(b'nobranch', _translate_nobranch_error)
4463
error_translators.register(b'norepository',
4464
lambda err, find, get_path: errors.NoRepositoryPresent(
4466
error_translators.register(b'UnlockableTransport',
4467
lambda err, find, get_path: errors.UnlockableTransport(
4468
find('bzrdir').root_transport))
4469
error_translators.register(b'TokenMismatch',
4470
lambda err, find, get_path: errors.TokenMismatch(
4471
find('token'), '(remote token)'))
4472
error_translators.register(b'Diverged',
4473
lambda err, find, get_path: errors.DivergedBranches(
4474
find('branch'), find('other_branch')))
4475
error_translators.register(b'NotStacked',
4476
lambda err, find, get_path: errors.NotStacked(branch=find('branch')))
4479
def _translate_PermissionDenied(err, find, get_path):
4481
if len(err.error_args) >= 2:
4482
extra = err.error_args[1].decode('utf-8')
4485
return errors.PermissionDenied(path, extra=extra)
4488
error_translators.register(b'PermissionDenied', _translate_PermissionDenied)
4489
error_translators.register(b'ReadError',
4490
lambda err, find, get_path: errors.ReadError(get_path()))
4491
error_translators.register(b'NoSuchFile',
4492
lambda err, find, get_path: errors.NoSuchFile(get_path()))
4493
error_translators.register(b'TokenLockingNotSupported',
4494
lambda err, find, get_path: errors.TokenLockingNotSupported(
4495
find('repository')))
4496
error_translators.register(b'UnsuspendableWriteGroup',
4497
lambda err, find, get_path: errors.UnsuspendableWriteGroup(
4498
repository=find('repository')))
4499
error_translators.register(b'UnresumableWriteGroup',
4500
lambda err, find, get_path: errors.UnresumableWriteGroup(
4501
repository=find('repository'), write_groups=err.error_args[0],
4502
reason=err.error_args[1]))
4503
error_translators.register(b'AlreadyControlDir',
4504
lambda err, find, get_path: errors.AlreadyControlDirError(get_path()))
4506
no_context_error_translators.register(b'GhostRevisionsHaveNoRevno',
4507
lambda err: errors.GhostRevisionsHaveNoRevno(*err.error_args))
4508
no_context_error_translators.register(b'IncompatibleRepositories',
4509
lambda err: errors.IncompatibleRepositories(
4510
err.error_args[0].decode('utf-8'), err.error_args[1].decode('utf-8'), err.error_args[2].decode('utf-8')))
4511
no_context_error_translators.register(b'LockContention',
4512
lambda err: errors.LockContention('(remote lock)'))
4513
no_context_error_translators.register(b'LockFailed',
4514
lambda err: errors.LockFailed(err.error_args[0].decode('utf-8'), err.error_args[1].decode('utf-8')))
4515
no_context_error_translators.register(b'TipChangeRejected',
4516
lambda err: errors.TipChangeRejected(err.error_args[0].decode('utf8')))
4517
no_context_error_translators.register(b'UnstackableBranchFormat',
4518
lambda err: branch.UnstackableBranchFormat(*err.error_args))
4519
no_context_error_translators.register(b'UnstackableRepositoryFormat',
4520
lambda err: errors.UnstackableRepositoryFormat(*err.error_args))
4521
no_context_error_translators.register(b'FileExists',
4522
lambda err: errors.FileExists(err.error_args[0].decode('utf-8')))
4523
no_context_error_translators.register(b'DirectoryNotEmpty',
4524
lambda err: errors.DirectoryNotEmpty(err.error_args[0].decode('utf-8')))
4525
no_context_error_translators.register(b'UnknownFormat',
4526
lambda err: errors.UnknownFormatError(
4527
err.error_args[0].decode('ascii'), err.error_args[0].decode('ascii')))
4528
no_context_error_translators.register(b'InvalidURL',
4529
lambda err: urlutils.InvalidURL(
4530
err.error_args[0].decode('utf-8'), err.error_args[1].decode('ascii')))
4533
def _translate_short_readv_error(err):
4534
args = err.error_args
4535
return errors.ShortReadvError(
4536
args[0].decode('utf-8'),
4537
int(args[1].decode('ascii')), int(args[2].decode('ascii')),
4538
int(args[3].decode('ascii')))
4541
no_context_error_translators.register(b'ShortReadvError',
4542
_translate_short_readv_error)
4545
def _translate_unicode_error(err):
4546
encoding = err.error_args[0].decode('ascii')
4547
val = err.error_args[1].decode('utf-8')
4548
start = int(err.error_args[2].decode('ascii'))
4549
end = int(err.error_args[3].decode('ascii'))
4550
reason = err.error_args[4].decode('utf-8')
4551
if val.startswith('u:'):
4552
val = val[2:].decode('utf-8')
4553
elif val.startswith('s:'):
4554
val = val[2:].decode('base64')
4555
if err.error_verb == 'UnicodeDecodeError':
4556
raise UnicodeDecodeError(encoding, val, start, end, reason)
4557
elif err.error_verb == 'UnicodeEncodeError':
4558
raise UnicodeEncodeError(encoding, val, start, end, reason)
4561
no_context_error_translators.register(b'UnicodeEncodeError',
4562
_translate_unicode_error)
4563
no_context_error_translators.register(b'UnicodeDecodeError',
4564
_translate_unicode_error)
4565
no_context_error_translators.register(b'ReadOnlyError',
4566
lambda err: errors.TransportNotPossible('readonly transport'))
4567
no_context_error_translators.register(b'MemoryError',
4568
lambda err: errors.BzrError("remote server out of memory\n"
4569
"Retry non-remotely, or contact the server admin for details."))
4570
no_context_error_translators.register(b'RevisionNotPresent',
4571
lambda err: errors.RevisionNotPresent(err.error_args[0].decode('utf-8'), err.error_args[1].decode('utf-8')))
4573
no_context_error_translators.register(b'BzrCheckError',
4574
lambda err: errors.BzrCheckError(msg=err.error_args[0].decode('utf-8')))
2887
if err.error_verb == 'IncompatibleRepositories':
2888
raise errors.IncompatibleRepositories(err.error_args[0],
2889
err.error_args[1], err.error_args[2])
2890
elif err.error_verb == 'NoSuchRevision':
2891
raise NoSuchRevision(find('branch'), err.error_args[0])
2892
elif err.error_verb == 'nosuchrevision':
2893
raise NoSuchRevision(find('repository'), err.error_args[0])
2894
elif err.error_verb == 'nobranch':
2895
if len(err.error_args) >= 1:
2896
extra = err.error_args[0]
2899
raise errors.NotBranchError(path=find('bzrdir').root_transport.base,
2901
elif err.error_verb == 'norepository':
2902
raise errors.NoRepositoryPresent(find('bzrdir'))
2903
elif err.error_verb == 'LockContention':
2904
raise errors.LockContention('(remote lock)')
2905
elif err.error_verb == 'UnlockableTransport':
2906
raise errors.UnlockableTransport(find('bzrdir').root_transport)
2907
elif err.error_verb == 'LockFailed':
2908
raise errors.LockFailed(err.error_args[0], err.error_args[1])
2909
elif err.error_verb == 'TokenMismatch':
2910
raise errors.TokenMismatch(find('token'), '(remote token)')
2911
elif err.error_verb == 'Diverged':
2912
raise errors.DivergedBranches(find('branch'), find('other_branch'))
2913
elif err.error_verb == 'TipChangeRejected':
2914
raise errors.TipChangeRejected(err.error_args[0].decode('utf8'))
2915
elif err.error_verb == 'UnstackableBranchFormat':
2916
raise errors.UnstackableBranchFormat(*err.error_args)
2917
elif err.error_verb == 'UnstackableRepositoryFormat':
2918
raise errors.UnstackableRepositoryFormat(*err.error_args)
2919
elif err.error_verb == 'NotStacked':
2920
raise errors.NotStacked(branch=find('branch'))
2921
elif err.error_verb == 'PermissionDenied':
2923
if len(err.error_args) >= 2:
2924
extra = err.error_args[1]
2927
raise errors.PermissionDenied(path, extra=extra)
2928
elif err.error_verb == 'ReadError':
2930
raise errors.ReadError(path)
2931
elif err.error_verb == 'NoSuchFile':
2933
raise errors.NoSuchFile(path)
2934
elif err.error_verb == 'FileExists':
2935
raise errors.FileExists(err.error_args[0])
2936
elif err.error_verb == 'DirectoryNotEmpty':
2937
raise errors.DirectoryNotEmpty(err.error_args[0])
2938
elif err.error_verb == 'ShortReadvError':
2939
args = err.error_args
2940
raise errors.ShortReadvError(
2941
args[0], int(args[1]), int(args[2]), int(args[3]))
2942
elif err.error_verb in ('UnicodeEncodeError', 'UnicodeDecodeError'):
2943
encoding = str(err.error_args[0]) # encoding must always be a string
2944
val = err.error_args[1]
2945
start = int(err.error_args[2])
2946
end = int(err.error_args[3])
2947
reason = str(err.error_args[4]) # reason must always be a string
2948
if val.startswith('u:'):
2949
val = val[2:].decode('utf-8')
2950
elif val.startswith('s:'):
2951
val = val[2:].decode('base64')
2952
if err.error_verb == 'UnicodeDecodeError':
2953
raise UnicodeDecodeError(encoding, val, start, end, reason)
2954
elif err.error_verb == 'UnicodeEncodeError':
2955
raise UnicodeEncodeError(encoding, val, start, end, reason)
2956
elif err.error_verb == 'ReadOnlyError':
2957
raise errors.TransportNotPossible('readonly transport')
2958
raise errors.UnknownErrorFromSmartServer(err)