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
1202
raise errors.UnexpectedSmartServerResponse(response)
1882
1204
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()
1205
# TODO: Option to control what format is created?
1207
dest_repo = self._real_repository._format.initialize(to_bzrdir,
1209
dest_repo.fetch(self, revision_id=revision_id)
1904
1210
return dest_repo
1906
# These methods are just thin shims to the VFS object for now.
1212
### These methods are just thin shims to the VFS object for now.
1908
1214
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]
1216
return self._real_repository.revision_tree(revision_id)
1917
1218
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)
1220
return self._real_repository.get_serializer_format()
1929
1222
def get_commit_builder(self, branch, parents, config, timestamp=None,
1930
1223
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()
1225
# FIXME: It ought to be possible to call this without immediately
1226
# triggering _ensure_real. For now it's the easiest thing to do.
1228
real_repo = self._real_repository
1229
builder = real_repo.get_commit_builder(branch, parents,
1230
config, timestamp=timestamp, timezone=timezone,
1231
committer=committer, revprops=revprops, revision_id=revision_id)
1956
1234
def add_fallback_repository(self, repository):
1957
1235
"""Add a repository to use for looking up data not held locally.
1996
1273
return self._real_repository.add_inventory(revid, inv, parents)
1998
1275
def add_inventory_by_delta(self, basis_revision_id, delta, new_revision_id,
1999
parents, basis_inv=None, propagate_caches=False):
1276
parents, basis_inv=None, propagate_caches=False):
2000
1277
self._ensure_real()
2001
1278
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)
1279
delta, new_revision_id, parents, basis_inv=basis_inv,
1280
propagate_caches=propagate_caches)
1282
def add_revision(self, rev_id, rev, inv=None, config=None):
1284
return self._real_repository.add_revision(
1285
rev_id, rev, inv=inv, config=config)
2031
1288
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
1289
self._ensure_real()
2080
return self._real_repository._iter_inventories(revision_ids, ordering)
1290
return self._real_repository.get_inventory(revision_id)
2082
1292
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()
1294
return self._real_repository.iter_inventories(revision_ids, ordering)
2158
1297
def get_revision(self, revision_id):
2159
with self.lock_read():
2160
return self.get_revisions([revision_id])[0]
1299
return self._real_repository.get_revision(revision_id)
2162
1301
def get_transaction(self):
2163
1302
self._ensure_real()
2164
1303
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)
1306
def clone(self, a_bzrdir, revision_id=None):
1308
return self._real_repository.clone(a_bzrdir, revision_id=revision_id)
2173
1310
def make_working_trees(self):
2174
1311
"""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'
1313
return self._real_repository.make_working_trees()
2186
1315
def refresh_data(self):
2187
"""Re-read any data needed to synchronise with disk.
1316
"""Re-read any data needed to to synchronise with disk.
2189
1318
This method is intended to be called after another repository instance
2190
1319
(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.
1320
repository. It may not be called during a write group, but may be
1321
called at any other time.
1323
if self.is_in_write_group():
1324
raise errors.InternalBzrError(
1325
"May not refresh_data while in a write group.")
2197
1326
if self._real_repository is not None:
2198
1327
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
1329
def revision_ids_to_search_result(self, result_set):
2204
1330
"""Convert a set of revision ids to a graph SearchResult."""
2205
1331
result_parents = set()
2206
for parents in viewvalues(self.get_graph().get_parent_map(result_set)):
1332
for parents in self.get_graph().get_parent_map(
1333
result_set).itervalues():
2207
1334
result_parents.update(parents)
2208
1335
included_keys = result_set.intersection(result_parents)
2209
1336
start_keys = result_set.difference(included_keys)
2210
1337
exclude_keys = result_parents.difference(result_set)
2211
result = vf_search.SearchResult(start_keys, exclude_keys,
2212
len(result_set), result_set)
1338
result = graph.SearchResult(start_keys, exclude_keys,
1339
len(result_set), result_set)
2215
def search_missing_revision_ids(self, other,
2216
find_ghosts=True, revision_ids=None, if_present_ids=None,
1343
def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
2218
1344
"""Return the revision ids that other has that this does not.
2220
1346
These are returned in topological order.
2222
1348
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)
1350
return repository.InterRepository.get(
1351
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):
1353
def fetch(self, source, revision_id=None, pb=None, find_ghosts=False,
2232
1355
# No base implementation to use as RemoteRepository is not a subclass
2233
1356
# of Repository; so this is a copy of Repository.fetch().
2234
1357
if fetch_spec is not None and revision_id is not None:
2272
1395
return self._real_repository._get_versioned_file_checker(
2273
1396
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([
2281
osutils.safe_file_id(file_id),
2283
osutils.safe_revision_id(revid)]))
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
1398
def iter_files_bytes(self, desired_files):
2327
1399
"""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)
1402
return self._real_repository.iter_files_bytes(desired_files)
2359
1404
def get_parent_map(self, revision_ids):
2360
"""See breezy.Graph.get_parent_map()."""
1405
"""See bzrlib.Graph.get_parent_map()."""
2361
1406
return self._make_parents_provider().get_parent_map(revision_ids)
2363
1408
def _get_parent_map_rpc(self, keys):
2480
1536
revision_graph[d[0]] = (NULL_REVISION,)
2481
1537
return revision_graph
2483
1540
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()
1542
return self._real_repository.get_signature_text(revision_id)
2504
1545
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)
1547
return self._real_repository._get_inventory_xml(revision_id)
2511
1549
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)
1551
return self._real_repository.reconcile(other=other, thorough=thorough)
2540
1553
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)
1555
return self._real_repository.all_revision_ids()
2571
1558
def get_deltas_for_revisions(self, revisions, specific_fileids=None):
2572
with self.lock_read():
2573
medium = self._client._medium
2574
if medium._is_remote_before((1, 2)):
2576
for delta in self._real_repository.get_deltas_for_revisions(
2577
revisions, specific_fileids):
2580
# Get the revision-ids of interest
2581
required_trees = set()
2582
for revision in revisions:
2583
required_trees.add(revision.revision_id)
2584
required_trees.update(revision.parent_ids[:1])
2586
# Get the matching filtered trees. Note that it's more
2587
# efficient to pass filtered trees to changes_from() rather
2588
# than doing the filtering afterwards. changes_from() could
2589
# arguably do the filtering itself but it's path-based, not
2590
# file-id based, so filtering before or afterwards is
2592
if specific_fileids is None:
2593
trees = dict((t.get_revision_id(), t) for
2594
t in self.revision_trees(required_trees))
2596
trees = dict((t.get_revision_id(), t) for
2597
t in self._filtered_revision_trees(required_trees,
2600
# Calculate the deltas
2601
for revision in revisions:
2602
if not revision.parent_ids:
2603
old_tree = self.revision_tree(_mod_revision.NULL_REVISION)
2605
old_tree = trees[revision.parent_ids[0]]
2606
yield trees[revision.revision_id].changes_from(old_tree)
2608
def get_revision_delta(self, revision_id):
2609
with self.lock_read():
2610
r = self.get_revision(revision_id)
2611
return list(self.get_deltas_for_revisions([r]))[0]
1560
return self._real_repository.get_deltas_for_revisions(revisions,
1561
specific_fileids=specific_fileids)
1564
def get_revision_delta(self, revision_id, specific_fileids=None):
1566
return self._real_repository.get_revision_delta(revision_id,
1567
specific_fileids=specific_fileids)
2613
1570
def revision_trees(self, revision_ids):
2614
with self.lock_read():
2615
inventories = self.iter_inventories(revision_ids)
2616
for inv in inventories:
2617
yield RemoteInventoryTree(self, inv, inv.revision_id)
1572
return self._real_repository.revision_trees(revision_ids)
2619
1575
def get_revision_reconcile(self, revision_id):
2620
with self.lock_read():
2622
return self._real_repository.get_revision_reconcile(revision_id)
1577
return self._real_repository.get_revision_reconcile(revision_id)
2624
1580
def check(self, revision_ids=None, callback_refs=None, check_repo=True):
2625
with self.lock_read():
2627
return self._real_repository.check(revision_ids=revision_ids,
2628
callback_refs=callback_refs, check_repo=check_repo)
1582
return self._real_repository.check(revision_ids=revision_ids,
1583
callback_refs=callback_refs, check_repo=check_repo)
2630
1585
def copy_content_into(self, destination, revision_id=None):
2631
"""Make a complete copy of the content in self into destination.
2633
This is a destructive operation! Do not use it on existing
2636
interrepo = _mod_repository.InterRepository.get(self, destination)
2637
return interrepo.copy_content(revision_id)
1587
return self._real_repository.copy_content_into(
1588
destination, revision_id=revision_id)
2639
1590
def _copy_repository_tarball(self, to_bzrdir, revision_id=None):
2640
1591
# get a tarball of the remote repository, and copy from that into the
1593
from bzrlib import osutils
2643
1595
# TODO: Maybe a progress bar while streaming the tarball?
2644
note(gettext("Copying repository content as tarball..."))
1596
note("Copying repository content as tarball...")
2645
1597
tar_file = self._get_tarball('bz2')
2646
1598
if tar_file is None:
2648
1600
destination = to_bzrdir.create_repository()
2650
1602
tar = tarfile.open('repository', fileobj=tar_file,
2652
1604
tmpdir = osutils.mkdtemp()
2654
tar.extractall(tmpdir)
2655
tmp_bzrdir = _mod_bzrdir.BzrDir.open(tmpdir)
1606
_extract_tar(tar, tmpdir)
1607
tmp_bzrdir = BzrDir.open(tmpdir)
2656
1608
tmp_repo = tmp_bzrdir.open_repository()
2657
1609
tmp_repo.copy_content_into(destination, revision_id)
2746
1687
self._ensure_real()
2747
1688
return self._real_repository.texts
2749
def _iter_revisions_rpc(self, revision_ids):
2750
body = b"\n".join(revision_ids)
2751
path = self.controldir._path_for_remote_call(self._client)
2752
response_tuple, response_handler = (
2753
self._call_with_body_bytes_expecting_body(
2754
b"Repository.iter_revisions", (path, ), body))
2755
if response_tuple[0] != b"ok":
2756
raise errors.UnexpectedSmartServerResponse(response_tuple)
2757
serializer_format = response_tuple[1].decode('ascii')
2758
serializer = serializer_format_registry.get(serializer_format)
2759
byte_stream = response_handler.read_streamed_body()
2760
decompressor = zlib.decompressobj()
2762
for bytes in byte_stream:
2763
chunks.append(decompressor.decompress(bytes))
2764
if decompressor.unused_data != b"":
2765
chunks.append(decompressor.flush())
2766
yield serializer.read_revision_from_string(b"".join(chunks))
2767
unused = decompressor.unused_data
2768
decompressor = zlib.decompressobj()
2769
chunks = [decompressor.decompress(unused)]
2770
chunks.append(decompressor.flush())
2771
text = b"".join(chunks)
2773
yield serializer.read_revision_from_string(b"".join(chunks))
2775
def iter_revisions(self, revision_ids):
2776
for rev_id in revision_ids:
2777
if not rev_id or not isinstance(rev_id, bytes):
2778
raise errors.InvalidRevisionId(
2779
revision_id=rev_id, branch=self)
2780
with self.lock_read():
2782
missing = set(revision_ids)
2783
for rev in self._iter_revisions_rpc(revision_ids):
2784
missing.remove(rev.revision_id)
2785
yield (rev.revision_id, rev)
2786
for fallback in self._fallback_repositories:
2789
for (revid, rev) in fallback.iter_revisions(missing):
2792
missing.remove(revid)
2793
for revid in missing:
2795
except errors.UnknownSmartMethod:
2797
for entry in self._real_repository.iter_revisions(revision_ids):
1691
def get_revisions(self, revision_ids):
1693
return self._real_repository.get_revisions(revision_ids)
2800
1695
def supports_rich_root(self):
2801
1696
return self._format.rich_root_data
1698
def iter_reverse_revision_history(self, revision_id):
1700
return self._real_repository.iter_reverse_revision_history(revision_id)
2804
1703
def _serializer(self):
2805
1704
return self._format._serializer
2807
1706
def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
2808
with self.lock_write():
2809
signature = gpg_strategy.sign(plaintext, gpg.MODE_CLEAR)
2810
self.add_signature_text(revision_id, signature)
1708
return self._real_repository.store_revision_signature(
1709
gpg_strategy, plaintext, revision_id)
2812
1711
def add_signature_text(self, revision_id, signature):
2813
if self._real_repository:
2814
# If there is a real repository the write group will
2815
# be in the real repository as well, so use that:
2817
return self._real_repository.add_signature_text(
2818
revision_id, signature)
2819
path = self.controldir._path_for_remote_call(self._client)
2820
response, handler = self._call_with_body_bytes_expecting_body(
2821
b'Repository.add_signature_text', (path, self._lock_token,
2823
tuple([token.encode('utf-8')
2824
for token in self._write_group_tokens]),
2826
handler.cancel_read_body()
2828
if response[0] != b'ok':
2829
raise errors.UnexpectedSmartServerResponse(response)
2830
self._write_group_tokens = [token.decode(
2831
'utf-8') for token in response[1:]]
1713
return self._real_repository.add_signature_text(revision_id, signature)
2833
1715
def has_signature_for_revision_id(self, revision_id):
2834
path = self.controldir._path_for_remote_call(self._client)
2836
response = self._call(b'Repository.has_signature_for_revision_id',
2838
except errors.UnknownSmartMethod:
2840
return self._real_repository.has_signature_for_revision_id(
2842
if response[0] not in (b'yes', b'no'):
2843
raise SmartProtocolError(
2844
'unexpected response code %s' % (response,))
2845
if response[0] == b'yes':
2847
for fallback in self._fallback_repositories:
2848
if fallback.has_signature_for_revision_id(revision_id):
2852
def verify_revision_signature(self, revision_id, gpg_strategy):
2853
with self.lock_read():
2854
if not self.has_signature_for_revision_id(revision_id):
2855
return gpg.SIGNATURE_NOT_SIGNED, None
2856
signature = self.get_signature_text(revision_id)
2858
testament = _mod_testament.Testament.from_revision(
2861
(status, key, signed_plaintext) = gpg_strategy.verify(signature)
2862
if testament.as_short_text() != signed_plaintext:
2863
return gpg.SIGNATURE_NOT_VALID, None
2864
return (status, key)
1717
return self._real_repository.has_signature_for_revision_id(revision_id)
2866
1719
def item_keys_introduced_by(self, revision_ids, _files_pb=None):
2867
1720
self._ensure_real()
2868
1721
return self._real_repository.item_keys_introduced_by(revision_ids,
2869
_files_pb=_files_pb)
1722
_files_pb=_files_pb)
1724
def revision_graph_can_have_wrong_parents(self):
1725
# The answer depends on the remote repo format.
1727
return self._real_repository.revision_graph_can_have_wrong_parents()
2871
1729
def _find_inconsistent_revision_parents(self, revisions_iterator=None):
2872
1730
self._ensure_real()
2890
1749
:param recipe: A search recipe (start, stop, count).
2891
1750
:return: Serialised bytes.
2893
start_keys = b' '.join(recipe[1])
2894
stop_keys = b' '.join(recipe[2])
2895
count = str(recipe[3]).encode('ascii')
2896
return b'\n'.join((start_keys, stop_keys, count))
1752
start_keys = ' '.join(recipe[1])
1753
stop_keys = ' '.join(recipe[2])
1754
count = str(recipe[3])
1755
return '\n'.join((start_keys, stop_keys, count))
2898
1757
def _serialise_search_result(self, search_result):
2899
parts = search_result.get_network_struct()
2900
return b'\n'.join(parts)
1758
if isinstance(search_result, graph.PendingAncestryResult):
1759
parts = ['ancestry-of']
1760
parts.extend(search_result.heads)
1762
recipe = search_result.get_recipe()
1763
parts = [recipe[0], self._serialise_search_recipe(recipe)]
1764
return '\n'.join(parts)
2902
1766
def autopack(self):
2903
path = self.controldir._path_for_remote_call(self._client)
1767
path = self.bzrdir._path_for_remote_call(self._client)
2905
response = self._call(b'PackRepository.autopack', path)
1769
response = self._call('PackRepository.autopack', path)
2906
1770
except errors.UnknownSmartMethod:
2907
1771
self._ensure_real()
2908
1772
self._real_repository._pack_collection.autopack()
2910
1774
self.refresh_data()
2911
if response[0] != b'ok':
2912
raise errors.UnexpectedSmartServerResponse(response)
2914
def _revision_archive(self, revision_id, format, name, root, subdir,
2916
path = self.controldir._path_for_remote_call(self._client)
2917
format = format or ''
2919
subdir = subdir or ''
2920
force_mtime = int(force_mtime) if force_mtime is not None else None
2922
response, protocol = self._call_expecting_body(
2923
b'Repository.revision_archive', path,
2925
format.encode('ascii'),
2926
os.path.basename(name).encode('utf-8'),
2927
root.encode('utf-8'),
2928
subdir.encode('utf-8'),
2930
except errors.UnknownSmartMethod:
2932
if response[0] == b'ok':
2933
return iter([protocol.read_body_bytes()])
2934
raise errors.UnexpectedSmartServerResponse(response)
2936
def _annotate_file_revision(self, revid, tree_path, file_id, default_revision):
2937
path = self.controldir._path_for_remote_call(self._client)
2938
tree_path = tree_path.encode('utf-8')
2939
file_id = file_id or b''
2940
default_revision = default_revision or b''
2942
response, handler = self._call_expecting_body(
2943
b'Repository.annotate_file_revision', path,
2944
revid, tree_path, file_id, default_revision)
2945
except errors.UnknownSmartMethod:
2947
if response[0] != b'ok':
2948
raise errors.UnexpectedSmartServerResponse(response)
2949
return map(tuple, bencode.bdecode(handler.read_body_bytes()))
2952
class RemoteStreamSink(vf_repository.StreamSink):
1775
if response[0] != 'ok':
1776
raise errors.UnexpectedSmartServerResponse(response)
1779
class RemoteStreamSink(repository.StreamSink):
2954
1781
def _insert_real(self, stream, src_format, resume_tokens):
2955
1782
self.target_repo._ensure_real()
3299
2066
def network_name(self):
3300
2067
return self._network_name
3302
def open(self, a_controldir, name=None, ignore_fallbacks=False):
3303
return a_controldir.open_branch(name=name,
3304
ignore_fallbacks=ignore_fallbacks)
2069
def open(self, a_bzrdir, name=None, ignore_fallbacks=False):
2070
return a_bzrdir.open_branch(name=name,
2071
ignore_fallbacks=ignore_fallbacks)
3306
def _vfs_initialize(self, a_controldir, name, append_revisions_only,
2073
def _vfs_initialize(self, a_bzrdir, name):
3308
2074
# Initialisation when using a local bzrdir object, or a non-vfs init
3309
2075
# method is not available on the server.
3310
2076
# self._custom_format is always set - the start of initialize ensures
3312
if isinstance(a_controldir, RemoteBzrDir):
3313
a_controldir._ensure_real()
3314
result = self._custom_format.initialize(a_controldir._real_bzrdir,
3315
name=name, append_revisions_only=append_revisions_only,
3316
repository=repository)
2078
if isinstance(a_bzrdir, RemoteBzrDir):
2079
a_bzrdir._ensure_real()
2080
result = self._custom_format.initialize(a_bzrdir._real_bzrdir,
3318
2083
# We assume the bzrdir is parameterised; it may not be.
3319
result = self._custom_format.initialize(a_controldir, name=name,
3320
append_revisions_only=append_revisions_only,
3321
repository=repository)
3322
if (isinstance(a_controldir, RemoteBzrDir)
3323
and not isinstance(result, RemoteBranch)):
3324
result = RemoteBranch(a_controldir, a_controldir.find_repository(), result,
2084
result = self._custom_format.initialize(a_bzrdir, name)
2085
if (isinstance(a_bzrdir, RemoteBzrDir) and
2086
not isinstance(result, RemoteBranch)):
2087
result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result,
3328
def initialize(self, a_controldir, name=None, repository=None,
3329
append_revisions_only=None):
3331
name = a_controldir._get_selected_branch()
2091
def initialize(self, a_bzrdir, name=None):
3332
2092
# 1) get the network name to use.
3333
2093
if self._custom_format:
3334
2094
network_name = self._custom_format.network_name()
3336
# Select the current breezy default and ask for that.
3337
reference_bzrdir_format = controldir.format_registry.get(
2096
# Select the current bzrlib default and ask for that.
2097
reference_bzrdir_format = bzrdir.format_registry.get('default')()
3339
2098
reference_format = reference_bzrdir_format.get_branch_format()
3340
2099
self._custom_format = reference_format
3341
2100
network_name = reference_format.network_name()
3342
2101
# Being asked to create on a non RemoteBzrDir:
3343
if not isinstance(a_controldir, RemoteBzrDir):
3344
return self._vfs_initialize(a_controldir, name=name,
3345
append_revisions_only=append_revisions_only,
3346
repository=repository)
3347
medium = a_controldir._client._medium
2102
if not isinstance(a_bzrdir, RemoteBzrDir):
2103
return self._vfs_initialize(a_bzrdir, name=name)
2104
medium = a_bzrdir._client._medium
3348
2105
if medium._is_remote_before((1, 13)):
3349
return self._vfs_initialize(a_controldir, name=name,
3350
append_revisions_only=append_revisions_only,
3351
repository=repository)
2106
return self._vfs_initialize(a_bzrdir, name=name)
3352
2107
# Creating on a remote bzr dir.
3353
2108
# 2) try direct creation via RPC
3354
path = a_controldir._path_for_remote_call(a_controldir._client)
2109
path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
2110
if name is not None:
3356
2111
# XXX JRV20100304: Support creating colocated branches
3357
2112
raise errors.NoColocatedBranchSupport(self)
3358
verb = b'BzrDir.create_branch'
2113
verb = 'BzrDir.create_branch'
3360
response = a_controldir._call(verb, path, network_name)
2115
response = a_bzrdir._call(verb, path, network_name)
3361
2116
except errors.UnknownSmartMethod:
3362
2117
# Fallback - use vfs methods
3363
2118
medium._remember_remote_is_before((1, 13))
3364
return self._vfs_initialize(a_controldir, name=name,
3365
append_revisions_only=append_revisions_only,
3366
repository=repository)
3367
if response[0] != b'ok':
2119
return self._vfs_initialize(a_bzrdir, name=name)
2120
if response[0] != 'ok':
3368
2121
raise errors.UnexpectedSmartServerResponse(response)
3369
2122
# Turn the response into a RemoteRepository object.
3370
2123
format = RemoteBranchFormat(network_name=response[1])
3371
2124
repo_format = response_tuple_to_repo_format(response[3:])
3372
repo_path = response[2].decode('utf-8')
3373
if repository is not None:
3374
remote_repo_url = urlutils.join(a_controldir.user_url, repo_path)
3375
url_diff = urlutils.relative_url(repository.user_url,
3378
raise AssertionError(
3379
'repository.user_url %r does not match URL from server '
3380
'response (%r + %r)'
3381
% (repository.user_url, a_controldir.user_url, repo_path))
3382
remote_repo = repository
2125
if response[2] == '':
2126
repo_bzrdir = a_bzrdir
3385
repo_bzrdir = a_controldir
3387
repo_bzrdir = RemoteBzrDir(
3388
a_controldir.root_transport.clone(
3389
repo_path), a_controldir._format,
3390
a_controldir._client)
3391
remote_repo = RemoteRepository(repo_bzrdir, repo_format)
3392
remote_branch = RemoteBranch(a_controldir, remote_repo,
3393
format=format, setup_stacking=False, name=name)
3394
if append_revisions_only:
3395
remote_branch.set_append_revisions_only(append_revisions_only)
2128
repo_bzrdir = RemoteBzrDir(
2129
a_bzrdir.root_transport.clone(response[2]), a_bzrdir._format,
2131
remote_repo = RemoteRepository(repo_bzrdir, repo_format)
2132
remote_branch = RemoteBranch(a_bzrdir, remote_repo,
2133
format=format, setup_stacking=False, name=name)
3396
2134
# XXX: We know this is a new branch, so it must have revno 0, revid
3397
2135
# NULL_REVISION. Creating the branch locked would make this be unable
3398
2136
# to be wrong; here its simply very unlikely to be wrong. RBC 20090225
3417
2155
self._ensure_real()
3418
2156
return self._custom_format.supports_set_append_revisions_only()
3421
def supports_reference_locations(self):
3423
return self._custom_format.supports_reference_locations
3425
def stores_revno(self):
3428
def _use_default_local_heads_to_fetch(self):
3429
# If the branch format is a metadir format *and* its heads_to_fetch
3430
# implementation is not overridden vs the base class, we can use the
3431
# base class logic rather than use the heads_to_fetch RPC. This is
3432
# usually cheaper in terms of net round trips, as the last-revision and
3433
# tags info fetched is cached and would be fetched anyway.
3435
if isinstance(self._custom_format, bzrbranch.BranchFormatMetadir):
3436
branch_class = self._custom_format._branch_class()
3437
heads_to_fetch_impl = get_unbound_function(
3438
branch_class.heads_to_fetch)
3439
if heads_to_fetch_impl is get_unbound_function(branch.Branch.heads_to_fetch):
3444
class RemoteBranchStore(_mod_config.IniFileStore):
3445
"""Branch store which attempts to use HPSS calls to retrieve branch store.
3447
Note that this is specific to bzr-based formats.
3450
def __init__(self, branch):
3451
super(RemoteBranchStore, self).__init__()
3452
self.branch = branch
3454
self._real_store = None
3456
def external_url(self):
3457
return urlutils.join(self.branch.user_url, 'branch.conf')
3459
def _load_content(self):
3460
path = self.branch._remote_path()
3462
response, handler = self.branch._call_expecting_body(
3463
b'Branch.get_config_file', path)
3464
except errors.UnknownSmartMethod:
3466
return self._real_store._load_content()
3467
if len(response) and response[0] != b'ok':
3468
raise errors.UnexpectedSmartServerResponse(response)
3469
return handler.read_body_bytes()
3471
def _save_content(self, content):
3472
path = self.branch._remote_path()
3474
response, handler = self.branch._call_with_body_bytes_expecting_body(
3475
b'Branch.put_config_file', (path,
3476
self.branch._lock_token, self.branch._repo_lock_token),
3478
except errors.UnknownSmartMethod:
3480
return self._real_store._save_content(content)
3481
handler.cancel_read_body()
3482
if response != (b'ok', ):
3483
raise errors.UnexpectedSmartServerResponse(response)
3485
def _ensure_real(self):
3486
self.branch._ensure_real()
3487
if self._real_store is None:
3488
self._real_store = _mod_config.BranchStore(self.branch)
3491
2159
class RemoteBranch(branch.Branch, _RpcHelper, lock._RelockDebugMixin):
3492
2160
"""Branch stored on a server accessed by HPSS RPC.
4043
2657
self._ensure_real()
4044
2658
return self._real_branch._set_parent_location(url)
4046
2661
def pull(self, source, overwrite=False, stop_revision=None,
4048
with self.lock_write():
4049
self._clear_cached_state_of_remote_branch_only()
4051
return self._real_branch.pull(
4052
source, overwrite=overwrite, stop_revision=stop_revision,
4053
_override_hook_target=self, **kwargs)
4055
def push(self, target, overwrite=False, stop_revision=None, lossy=False, tag_selector=None):
4056
with self.lock_read():
4058
return self._real_branch.push(
4059
target, overwrite=overwrite, stop_revision=stop_revision, lossy=lossy,
4060
_override_hook_source_branch=self, tag_selector=tag_selector)
4062
def peek_lock_mode(self):
4063
return self._lock_mode
2663
self._clear_cached_state_of_remote_branch_only()
2665
return self._real_branch.pull(
2666
source, overwrite=overwrite, stop_revision=stop_revision,
2667
_override_hook_target=self, **kwargs)
2670
def push(self, target, overwrite=False, stop_revision=None):
2672
return self._real_branch.push(
2673
target, overwrite=overwrite, stop_revision=stop_revision,
2674
_override_hook_source_branch=self)
4065
2676
def is_locked(self):
4066
2677
return self._lock_count >= 1
4068
def revision_id_to_dotted_revno(self, revision_id):
4069
"""Given a revision id, return its dotted revno.
4071
:return: a tuple like (1,) or (400,1,3).
4073
with self.lock_read():
4075
response = self._call(b'Branch.revision_id_to_revno',
4076
self._remote_path(), revision_id)
4077
except errors.UnknownSmartMethod:
4079
return self._real_branch.revision_id_to_dotted_revno(revision_id)
4080
except errors.UnknownErrorFromSmartServer as e:
4081
# Deal with older versions of bzr/brz that didn't explicitly
4082
# wrap GhostRevisionsHaveNoRevno.
4083
if e.error_tuple[1] == b'GhostRevisionsHaveNoRevno':
4084
(revid, ghost_revid) = re.findall(b"{([^}]+)}", e.error_tuple[2])
4085
raise errors.GhostRevisionsHaveNoRevno(
4088
if response[0] == b'ok':
4089
return tuple([int(x) for x in response[1:]])
4091
raise errors.UnexpectedSmartServerResponse(response)
4093
2680
def revision_id_to_revno(self, revision_id):
4094
"""Given a revision id on the branch mainline, return its revno.
4098
with self.lock_read():
4100
response = self._call(b'Branch.revision_id_to_revno',
4101
self._remote_path(), revision_id)
4102
except errors.UnknownSmartMethod:
4104
return self._real_branch.revision_id_to_revno(revision_id)
4105
if response[0] == b'ok':
4106
if len(response) == 2:
4107
return int(response[1])
4108
raise NoSuchRevision(self, revision_id)
4110
raise errors.UnexpectedSmartServerResponse(response)
2682
return self._real_branch.revision_id_to_revno(revision_id)
4112
2685
def set_last_revision_info(self, revno, revision_id):
4113
with self.lock_write():
4114
# XXX: These should be returned by the set_last_revision_info verb
4115
old_revno, old_revid = self.last_revision_info()
4116
self._run_pre_change_branch_tip_hooks(revno, revision_id)
4117
if not revision_id or not isinstance(revision_id, bytes):
4118
raise errors.InvalidRevisionId(
4119
revision_id=revision_id, branch=self)
4121
response = self._call(b'Branch.set_last_revision_info',
4122
self._remote_path(), self._lock_token, self._repo_lock_token,
4123
str(revno).encode('ascii'), revision_id)
4124
except errors.UnknownSmartMethod:
4126
self._clear_cached_state_of_remote_branch_only()
4127
self._real_branch.set_last_revision_info(revno, revision_id)
4128
self._last_revision_info_cache = revno, revision_id
4130
if response == (b'ok',):
4131
self._clear_cached_state()
4132
self._last_revision_info_cache = revno, revision_id
4133
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
4134
# Update the _real_branch's cache too.
4135
if self._real_branch is not None:
4136
cache = self._last_revision_info_cache
4137
self._real_branch._last_revision_info_cache = cache
4139
raise errors.UnexpectedSmartServerResponse(response)
2686
# XXX: These should be returned by the set_last_revision_info verb
2687
old_revno, old_revid = self.last_revision_info()
2688
self._run_pre_change_branch_tip_hooks(revno, revision_id)
2689
revision_id = ensure_null(revision_id)
2691
response = self._call('Branch.set_last_revision_info',
2692
self._remote_path(), self._lock_token, self._repo_lock_token,
2693
str(revno), revision_id)
2694
except errors.UnknownSmartMethod:
2696
self._clear_cached_state_of_remote_branch_only()
2697
self._real_branch.set_last_revision_info(revno, revision_id)
2698
self._last_revision_info_cache = revno, revision_id
2700
if response == ('ok',):
2701
self._clear_cached_state()
2702
self._last_revision_info_cache = revno, revision_id
2703
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2704
# Update the _real_branch's cache too.
2705
if self._real_branch is not None:
2706
cache = self._last_revision_info_cache
2707
self._real_branch._last_revision_info_cache = cache
2709
raise errors.UnexpectedSmartServerResponse(response)
4141
2712
def generate_revision_history(self, revision_id, last_rev=None,
4142
2713
other_branch=None):
4143
with self.lock_write():
4144
medium = self._client._medium
4145
if not medium._is_remote_before((1, 6)):
4146
# Use a smart method for 1.6 and above servers
4148
self._set_last_revision_descendant(revision_id, other_branch,
4149
allow_diverged=True, allow_overwrite_descendant=True)
4151
except errors.UnknownSmartMethod:
4152
medium._remember_remote_is_before((1, 6))
4153
self._clear_cached_state_of_remote_branch_only()
4154
graph = self.repository.get_graph()
4155
(last_revno, last_revid) = self.last_revision_info()
4156
known_revision_ids = [
4157
(last_revid, last_revno),
4158
(_mod_revision.NULL_REVISION, 0),
4160
if last_rev is not None:
4161
if not graph.is_ancestor(last_rev, revision_id):
4162
# our previous tip is not merged into stop_revision
4163
raise errors.DivergedBranches(self, other_branch)
4164
revno = graph.find_distance_to_null(
4165
revision_id, known_revision_ids)
4166
self.set_last_revision_info(revno, revision_id)
2714
medium = self._client._medium
2715
if not medium._is_remote_before((1, 6)):
2716
# Use a smart method for 1.6 and above servers
2718
self._set_last_revision_descendant(revision_id, other_branch,
2719
allow_diverged=True, allow_overwrite_descendant=True)
2721
except errors.UnknownSmartMethod:
2722
medium._remember_remote_is_before((1, 6))
2723
self._clear_cached_state_of_remote_branch_only()
2724
self.set_revision_history(self._lefthand_history(revision_id,
2725
last_rev=last_rev,other_branch=other_branch))
4168
2727
def set_push_location(self, location):
4169
self._set_config_location('push_location', location)
4171
def heads_to_fetch(self):
4172
if self._format._use_default_local_heads_to_fetch():
4173
# We recognise this format, and its heads-to-fetch implementation
4174
# is the default one (tip + tags). In this case it's cheaper to
4175
# just use the default implementation rather than a special RPC as
4176
# the tip and tags data is cached.
4177
return branch.Branch.heads_to_fetch(self)
4178
medium = self._client._medium
4179
if medium._is_remote_before((2, 4)):
4180
return self._vfs_heads_to_fetch()
4182
return self._rpc_heads_to_fetch()
4183
except errors.UnknownSmartMethod:
4184
medium._remember_remote_is_before((2, 4))
4185
return self._vfs_heads_to_fetch()
4187
def _rpc_heads_to_fetch(self):
4188
response = self._call(b'Branch.heads_to_fetch', self._remote_path())
4189
if len(response) != 2:
4190
raise errors.UnexpectedSmartServerResponse(response)
4191
must_fetch, if_present_fetch = response
4192
return set(must_fetch), set(if_present_fetch)
4194
def _vfs_heads_to_fetch(self):
4196
return self._real_branch.heads_to_fetch()
4198
def reconcile(self, thorough=True):
4199
"""Make sure the data stored in this branch is consistent."""
4200
from .reconcile import BranchReconciler
4201
with self.lock_write():
4202
reconciler = BranchReconciler(self, thorough=thorough)
4203
return reconciler.reconcile()
4205
def get_reference_info(self, file_id):
4206
"""Get the tree_path and branch_location for a tree reference."""
4207
if not self._format.supports_reference_locations:
4208
raise errors.UnsupportedOperation(self.get_reference_info, self)
4209
return self._get_all_reference_info().get(file_id, (None, None))
4211
def set_reference_info(self, file_id, branch_location, tree_path=None):
4212
"""Set the branch location to use for a tree reference."""
4213
if not self._format.supports_reference_locations:
4214
raise errors.UnsupportedOperation(self.set_reference_info, self)
4216
self._real_branch.set_reference_info(
4217
file_id, branch_location, tree_path)
4219
def _set_all_reference_info(self, reference_info):
4220
if not self._format.supports_reference_locations:
4221
raise errors.UnsupportedOperation(self.set_reference_info, self)
4223
self._real_branch._set_all_reference_info(reference_info)
4225
def _get_all_reference_info(self):
4226
if not self._format.supports_reference_locations:
4229
response, handler = self._call_expecting_body(
4230
b'Branch.get_all_reference_info', self._remote_path())
4231
except errors.UnknownSmartMethod:
4233
return self._real_branch._get_all_reference_info()
4234
if len(response) and response[0] != b'ok':
4235
raise errors.UnexpectedSmartServerResponse(response)
4237
for (f, u, p) in bencode.bdecode(handler.read_body_bytes()):
4238
ret[f] = (u.decode('utf-8'), p.decode('utf-8') if p else None)
4241
def reference_parent(self, file_id, path, possible_transports=None):
4242
"""Return the parent branch for a tree-reference.
4244
:param path: The path of the nested tree in the tree
4245
:return: A branch associated with the nested tree
4247
branch_location = self.get_reference_info(file_id)[0]
4248
if branch_location is None:
4250
return branch.Branch.open_from_transport(
4251
self.controldir.root_transport.clone(path),
4252
possible_transports=possible_transports)
4253
except errors.NotBranchError:
4255
return branch.Branch.open(
4257
urlutils.strip_segment_parameters(self.user_url), branch_location),
4258
possible_transports=possible_transports)
2729
return self._real_branch.set_push_location(location)
4261
2732
class RemoteConfig(object):
4326
2787
medium = self._branch._client._medium
4327
2788
if medium._is_remote_before((1, 14)):
4328
2789
return self._vfs_set_option(value, name, section)
4329
if isinstance(value, dict):
4330
if medium._is_remote_before((2, 2)):
4331
return self._vfs_set_option(value, name, section)
4332
return self._set_config_option_dict(value, name, section)
4334
return self._set_config_option(value, name, section)
4336
def _set_config_option(self, value, name, section):
4337
if isinstance(value, (bool, int)):
4339
elif isinstance(value, (text_type, str)):
4342
raise TypeError(value)
4344
2791
path = self._branch._remote_path()
4345
response = self._branch._client.call(b'Branch.set_config_option',
4346
path, self._branch._lock_token, self._branch._repo_lock_token,
4347
value.encode('utf-8'), name.encode('utf-8'),
4348
(section or '').encode('utf-8'))
2792
response = self._branch._client.call('Branch.set_config_option',
2793
path, self._branch._lock_token, self._branch._repo_lock_token,
2794
value.encode('utf8'), name, section or '')
4349
2795
except errors.UnknownSmartMethod:
4350
medium = self._branch._client._medium
4351
2796
medium._remember_remote_is_before((1, 14))
4352
2797
return self._vfs_set_option(value, name, section)
4353
2798
if response != ():
4354
2799
raise errors.UnexpectedSmartServerResponse(response)
4356
def _serialize_option_dict(self, option_dict):
4358
for key, value in option_dict.items():
4359
if isinstance(key, text_type):
4360
key = key.encode('utf8')
4361
if isinstance(value, text_type):
4362
value = value.encode('utf8')
4363
utf8_dict[key] = value
4364
return bencode.bencode(utf8_dict)
4366
def _set_config_option_dict(self, value, name, section):
4368
path = self._branch._remote_path()
4369
serialised_dict = self._serialize_option_dict(value)
4370
response = self._branch._client.call(
4371
b'Branch.set_config_option_dict',
4372
path, self._branch._lock_token, self._branch._repo_lock_token,
4373
serialised_dict, name.encode('utf-8'), (section or '').encode('utf-8'))
4374
except errors.UnknownSmartMethod:
4375
medium = self._branch._client._medium
4376
medium._remember_remote_is_before((2, 2))
4377
return self._vfs_set_option(value, name, section)
4379
raise errors.UnexpectedSmartServerResponse(response)
4381
2801
def _real_object(self):
4382
2802
self._branch._ensure_real()
4383
2803
return self._branch._real_branch
4443
2869
def find(name):
4445
2871
return context[name]
4447
mutter('Missing key \'%s\' in context %r', name, context)
2872
except KeyError, key_err:
2873
mutter('Missing key %r in context %r', key_err.args[0], context)
4450
2875
def get_path():
4451
2876
"""Get the path from the context if present, otherwise use first error
4455
2880
return context['path']
2881
except KeyError, key_err:
4458
return err.error_args[0].decode('utf-8')
4460
mutter('Missing key \'path\' in context %r', context)
2883
return err.error_args[0]
2884
except IndexError, idx_err:
2886
'Missing key %r in context %r', key_err.args[0], context)
4462
if not isinstance(err.error_verb, bytes):
4463
raise TypeError(err.error_verb)
4465
translator = error_translators.get(err.error_verb)
4469
raise translator(err, find, get_path)
4471
translator = no_context_error_translators.get(err.error_verb)
4473
raise errors.UnknownErrorFromSmartServer(err)
4475
raise translator(err)
4478
error_translators.register(b'NoSuchRevision',
4479
lambda err, find, get_path: NoSuchRevision(
4480
find('branch'), err.error_args[0]))
4481
error_translators.register(b'nosuchrevision',
4482
lambda err, find, get_path: NoSuchRevision(
4483
find('repository'), err.error_args[0]))
4484
error_translators.register(
4485
b'revno-outofbounds',
4486
lambda err, find, get_path: errors.RevnoOutOfBounds(
4487
err.error_args[0], (err.error_args[1], err.error_args[2])))
4490
def _translate_nobranch_error(err, find, get_path):
4491
if len(err.error_args) >= 1:
4492
extra = err.error_args[0].decode('utf-8')
4495
return errors.NotBranchError(path=find('bzrdir').root_transport.base,
4499
error_translators.register(b'nobranch', _translate_nobranch_error)
4500
error_translators.register(b'norepository',
4501
lambda err, find, get_path: errors.NoRepositoryPresent(
4503
error_translators.register(b'UnlockableTransport',
4504
lambda err, find, get_path: errors.UnlockableTransport(
4505
find('bzrdir').root_transport))
4506
error_translators.register(b'TokenMismatch',
4507
lambda err, find, get_path: errors.TokenMismatch(
4508
find('token'), '(remote token)'))
4509
error_translators.register(b'Diverged',
4510
lambda err, find, get_path: errors.DivergedBranches(
4511
find('branch'), find('other_branch')))
4512
error_translators.register(b'NotStacked',
4513
lambda err, find, get_path: errors.NotStacked(branch=find('branch')))
4516
def _translate_PermissionDenied(err, find, get_path):
4518
if len(err.error_args) >= 2:
4519
extra = err.error_args[1].decode('utf-8')
4522
return errors.PermissionDenied(path, extra=extra)
4525
error_translators.register(b'PermissionDenied', _translate_PermissionDenied)
4526
error_translators.register(b'ReadError',
4527
lambda err, find, get_path: errors.ReadError(get_path()))
4528
error_translators.register(b'NoSuchFile',
4529
lambda err, find, get_path: errors.NoSuchFile(get_path()))
4530
error_translators.register(b'TokenLockingNotSupported',
4531
lambda err, find, get_path: errors.TokenLockingNotSupported(
4532
find('repository')))
4533
error_translators.register(b'UnsuspendableWriteGroup',
4534
lambda err, find, get_path: errors.UnsuspendableWriteGroup(
4535
repository=find('repository')))
4536
error_translators.register(b'UnresumableWriteGroup',
4537
lambda err, find, get_path: errors.UnresumableWriteGroup(
4538
repository=find('repository'), write_groups=err.error_args[0],
4539
reason=err.error_args[1]))
4540
no_context_error_translators.register(b'GhostRevisionsHaveNoRevno',
4541
lambda err: errors.GhostRevisionsHaveNoRevno(*err.error_args))
4542
no_context_error_translators.register(b'IncompatibleRepositories',
4543
lambda err: errors.IncompatibleRepositories(
4544
err.error_args[0].decode('utf-8'), err.error_args[1].decode('utf-8'), err.error_args[2].decode('utf-8')))
4545
no_context_error_translators.register(b'LockContention',
4546
lambda err: errors.LockContention('(remote lock)'))
4547
no_context_error_translators.register(b'LockFailed',
4548
lambda err: errors.LockFailed(err.error_args[0].decode('utf-8'), err.error_args[1].decode('utf-8')))
4549
no_context_error_translators.register(b'TipChangeRejected',
4550
lambda err: errors.TipChangeRejected(err.error_args[0].decode('utf8')))
4551
no_context_error_translators.register(b'UnstackableBranchFormat',
4552
lambda err: branch.UnstackableBranchFormat(*err.error_args))
4553
no_context_error_translators.register(b'UnstackableRepositoryFormat',
4554
lambda err: errors.UnstackableRepositoryFormat(*err.error_args))
4555
no_context_error_translators.register(b'FileExists',
4556
lambda err: errors.FileExists(err.error_args[0].decode('utf-8')))
4557
no_context_error_translators.register(b'DirectoryNotEmpty',
4558
lambda err: errors.DirectoryNotEmpty(err.error_args[0].decode('utf-8')))
4559
no_context_error_translators.register(b'UnknownFormat',
4560
lambda err: errors.UnknownFormatError(
4561
err.error_args[0].decode('ascii'), err.error_args[0].decode('ascii')))
4562
no_context_error_translators.register(b'InvalidURL',
4563
lambda err: urlutils.InvalidURL(
4564
err.error_args[0].decode('utf-8'), err.error_args[1].decode('ascii')))
4567
def _translate_short_readv_error(err):
4568
args = err.error_args
4569
return errors.ShortReadvError(
4570
args[0].decode('utf-8'),
4571
int(args[1].decode('ascii')), int(args[2].decode('ascii')),
4572
int(args[3].decode('ascii')))
4575
no_context_error_translators.register(b'ShortReadvError',
4576
_translate_short_readv_error)
4579
def _translate_unicode_error(err):
4580
encoding = err.error_args[0].decode('ascii')
4581
val = err.error_args[1].decode('utf-8')
4582
start = int(err.error_args[2].decode('ascii'))
4583
end = int(err.error_args[3].decode('ascii'))
4584
reason = err.error_args[4].decode('utf-8')
4585
if val.startswith('u:'):
4586
val = val[2:].decode('utf-8')
4587
elif val.startswith('s:'):
4588
val = val[2:].decode('base64')
4589
if err.error_verb == 'UnicodeDecodeError':
4590
raise UnicodeDecodeError(encoding, val, start, end, reason)
4591
elif err.error_verb == 'UnicodeEncodeError':
4592
raise UnicodeEncodeError(encoding, val, start, end, reason)
4595
no_context_error_translators.register(b'UnicodeEncodeError',
4596
_translate_unicode_error)
4597
no_context_error_translators.register(b'UnicodeDecodeError',
4598
_translate_unicode_error)
4599
no_context_error_translators.register(b'ReadOnlyError',
4600
lambda err: errors.TransportNotPossible('readonly transport'))
4601
no_context_error_translators.register(b'MemoryError',
4602
lambda err: errors.BzrError("remote server out of memory\n"
4603
"Retry non-remotely, or contact the server admin for details."))
4604
no_context_error_translators.register(b'RevisionNotPresent',
4605
lambda err: errors.RevisionNotPresent(err.error_args[0].decode('utf-8'), err.error_args[1].decode('utf-8')))
4607
no_context_error_translators.register(b'BzrCheckError',
4608
lambda err: errors.BzrCheckError(msg=err.error_args[0].decode('utf-8')))
2889
if err.error_verb == 'IncompatibleRepositories':
2890
raise errors.IncompatibleRepositories(err.error_args[0],
2891
err.error_args[1], err.error_args[2])
2892
elif err.error_verb == 'NoSuchRevision':
2893
raise NoSuchRevision(find('branch'), err.error_args[0])
2894
elif err.error_verb == 'nosuchrevision':
2895
raise NoSuchRevision(find('repository'), err.error_args[0])
2896
elif err.error_verb == 'nobranch':
2897
if len(err.error_args) >= 1:
2898
extra = err.error_args[0]
2901
raise errors.NotBranchError(path=find('bzrdir').root_transport.base,
2903
elif err.error_verb == 'norepository':
2904
raise errors.NoRepositoryPresent(find('bzrdir'))
2905
elif err.error_verb == 'LockContention':
2906
raise errors.LockContention('(remote lock)')
2907
elif err.error_verb == 'UnlockableTransport':
2908
raise errors.UnlockableTransport(find('bzrdir').root_transport)
2909
elif err.error_verb == 'LockFailed':
2910
raise errors.LockFailed(err.error_args[0], err.error_args[1])
2911
elif err.error_verb == 'TokenMismatch':
2912
raise errors.TokenMismatch(find('token'), '(remote token)')
2913
elif err.error_verb == 'Diverged':
2914
raise errors.DivergedBranches(find('branch'), find('other_branch'))
2915
elif err.error_verb == 'TipChangeRejected':
2916
raise errors.TipChangeRejected(err.error_args[0].decode('utf8'))
2917
elif err.error_verb == 'UnstackableBranchFormat':
2918
raise errors.UnstackableBranchFormat(*err.error_args)
2919
elif err.error_verb == 'UnstackableRepositoryFormat':
2920
raise errors.UnstackableRepositoryFormat(*err.error_args)
2921
elif err.error_verb == 'NotStacked':
2922
raise errors.NotStacked(branch=find('branch'))
2923
elif err.error_verb == 'PermissionDenied':
2925
if len(err.error_args) >= 2:
2926
extra = err.error_args[1]
2929
raise errors.PermissionDenied(path, extra=extra)
2930
elif err.error_verb == 'ReadError':
2932
raise errors.ReadError(path)
2933
elif err.error_verb == 'NoSuchFile':
2935
raise errors.NoSuchFile(path)
2936
elif err.error_verb == 'FileExists':
2937
raise errors.FileExists(err.error_args[0])
2938
elif err.error_verb == 'DirectoryNotEmpty':
2939
raise errors.DirectoryNotEmpty(err.error_args[0])
2940
elif err.error_verb == 'ShortReadvError':
2941
args = err.error_args
2942
raise errors.ShortReadvError(
2943
args[0], int(args[1]), int(args[2]), int(args[3]))
2944
elif err.error_verb in ('UnicodeEncodeError', 'UnicodeDecodeError'):
2945
encoding = str(err.error_args[0]) # encoding must always be a string
2946
val = err.error_args[1]
2947
start = int(err.error_args[2])
2948
end = int(err.error_args[3])
2949
reason = str(err.error_args[4]) # reason must always be a string
2950
if val.startswith('u:'):
2951
val = val[2:].decode('utf-8')
2952
elif val.startswith('s:'):
2953
val = val[2:].decode('base64')
2954
if err.error_verb == 'UnicodeDecodeError':
2955
raise UnicodeDecodeError(encoding, val, start, end, reason)
2956
elif err.error_verb == 'UnicodeEncodeError':
2957
raise UnicodeEncodeError(encoding, val, start, end, reason)
2958
elif err.error_verb == 'ReadOnlyError':
2959
raise errors.TransportNotPossible('readonly transport')
2960
raise errors.UnknownErrorFromSmartServer(err)