105
73
return self._client.call_with_body_bytes_expecting_body(
106
74
method, args, body_bytes)
107
except errors.ErrorFromSmartServer as err:
75
except errors.ErrorFromSmartServer, err:
108
76
self._translate_error(err, **err_context)
111
79
def response_tuple_to_repo_format(response):
112
80
"""Convert a response tuple describing a repository format to a format."""
113
81
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')
82
format._rich_root_data = (response[0] == 'yes')
83
format._supports_tree_reference = (response[1] == 'yes')
84
format._supports_external_lookups = (response[2] == 'yes')
117
85
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):
89
# Note: RemoteBzrDirFormat is in bzrdir.py
91
class RemoteBzrDir(BzrDir, _RpcHelper):
428
92
"""Control directory on a remote server, accessed via bzr:// or similar."""
430
94
def __init__(self, transport, format, _client=None, _force_probe=False):
564
186
medium = self._client._medium
565
187
if medium._is_remote_before((1, 13)):
566
188
return self._vfs_cloning_metadir(require_stacking=require_stacking)
567
verb = b'BzrDir.cloning_metadir'
189
verb = 'BzrDir.cloning_metadir'
568
190
if require_stacking:
572
194
path = self._path_for_remote_call(self._client)
574
196
response = self._call(verb, path, stacking)
575
197
except errors.UnknownSmartMethod:
576
198
medium._remember_remote_is_before((1, 13))
577
199
return self._vfs_cloning_metadir(require_stacking=require_stacking)
578
except errors.UnknownErrorFromSmartServer as err:
579
if err.error_tuple != (b'BranchReference',):
200
except errors.UnknownErrorFromSmartServer, err:
201
if err.error_tuple != ('BranchReference',):
581
203
# We need to resolve the branch reference to determine the
582
204
# cloning_metadir. This causes unnecessary RPCs to open the
583
205
# referenced branch (and bzrdir, etc) but only when the caller
584
206
# didn't already resolve the branch reference.
585
207
referenced_branch = self.open_branch()
586
return referenced_branch.controldir.cloning_metadir()
208
return referenced_branch.bzrdir.cloning_metadir()
587
209
if len(response) != 3:
588
210
raise errors.UnexpectedSmartServerResponse(response)
589
211
control_name, repo_name, branch_info = response
590
212
if len(branch_info) != 2:
591
213
raise errors.UnexpectedSmartServerResponse(response)
592
214
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)
215
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':
217
format.repository_format = repository.network_format_registry.get(
219
if branch_ref == 'ref':
607
220
# XXX: we need possible_transports here to avoid reopening the
608
221
# connection to the referenced location
609
ref_bzrdir = _mod_bzrdir.BzrDir.open(branch_name)
222
ref_bzrdir = BzrDir.open(branch_name)
610
223
branch_format = ref_bzrdir.cloning_metadir().get_branch_format()
611
224
format.set_branch_format(branch_format)
612
elif branch_ref == b'branch':
225
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)
227
format.set_branch_format(
228
branch.network_format_registry.get(branch_name))
622
230
raise errors.UnexpectedSmartServerResponse(response)
675
266
def destroy_branch(self, name=None):
676
267
"""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
269
self._real_bzrdir.destroy_branch(name=name)
693
270
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):
272
def create_workingtree(self, revision_id=None, from_branch=None):
700
273
raise errors.NotLocalUrl(self.transport.base)
702
def find_branch_format(self, name=None):
275
def find_branch_format(self):
703
276
"""Find the branch 'format' for this bzrdir.
705
278
This might be a synthetic object for e.g. RemoteBranch and SVN.
707
b = self.open_branch(name=name)
280
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):
283
def get_branch_reference(self):
757
284
"""See BzrDir.get_branch_reference()."""
759
name = self._get_selected_branch()
761
raise errors.NoColocatedBranchSupport(self)
762
285
response = self._get_branch_reference()
763
286
if response[0] == 'ref':
764
return response[1].decode('utf-8')
768
291
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
292
path = self._path_for_remote_call(self._client)
776
293
medium = self._client._medium
777
294
candidate_calls = [
778
(b'BzrDir.open_branchV3', (2, 1)),
779
(b'BzrDir.open_branchV2', (1, 13)),
780
(b'BzrDir.open_branch', None),
295
('BzrDir.open_branchV3', (2, 1)),
296
('BzrDir.open_branchV2', (1, 13)),
297
('BzrDir.open_branch', None),
782
299
for verb, required_version in candidate_calls:
783
300
if required_version and medium._is_remote_before(required_version):
790
307
medium._remember_remote_is_before(required_version)
793
if verb == b'BzrDir.open_branch':
794
if response[0] != b'ok':
310
if verb == 'BzrDir.open_branch':
311
if response[0] != 'ok':
795
312
raise errors.UnexpectedSmartServerResponse(response)
796
if response[1] != b'':
313
if response[1] != '':
797
314
return ('ref', response[1])
799
return ('branch', b'')
800
if response[0] not in (b'ref', b'branch'):
316
return ('branch', '')
317
if response[0] not in ('ref', 'branch'):
801
318
raise errors.UnexpectedSmartServerResponse(response)
802
return (response[0].decode('ascii'), response[1])
804
def _get_tree_branch(self, name=None):
321
def _get_tree_branch(self):
805
322
"""See BzrDir._get_tree_branch()."""
806
return None, self.open_branch(name=name)
323
return None, self.open_branch()
808
def _open_branch(self, name, kind, location_or_format,
809
ignore_fallbacks=False, possible_transports=None):
325
def open_branch(self, name=None, unsupported=False,
326
ignore_fallbacks=False):
328
raise NotImplementedError('unsupported flag support not implemented yet.')
329
if self._next_open_branch_result is not None:
330
# See create_branch for details.
331
result = self._next_open_branch_result
332
self._next_open_branch_result = None
334
response = self._get_branch_reference()
335
if response[0] == 'ref':
811
336
# a branch reference, use the existing BranchReference logic.
812
337
format = BranchReferenceFormat()
813
ref_loc = urlutils.join(self.user_url, location_or_format.decode('utf-8'))
814
338
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
339
location=response[1], ignore_fallbacks=ignore_fallbacks)
340
branch_format_name = response[1]
819
341
if not branch_format_name:
820
342
branch_format_name = None
821
343
format = RemoteBranchFormat(network_name=branch_format_name)
822
344
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)
345
setup_stacking=not ignore_fallbacks, name=name)
845
347
def _open_repo_v1(self, path):
846
verb = b'BzrDir.find_repository'
348
verb = 'BzrDir.find_repository'
847
349
response = self._call(verb, path)
848
if response[0] != b'ok':
350
if response[0] != 'ok':
849
351
raise errors.UnexpectedSmartServerResponse(response)
850
352
# servers that only support the v1 method don't support external
851
353
# references either.
852
354
self._ensure_real()
853
355
repo = self._real_bzrdir.open_repository()
854
response = response + (b'no', repo._format.network_name())
356
response = response + ('no', repo._format.network_name())
855
357
return response, repo
857
359
def _open_repo_v2(self, path):
858
verb = b'BzrDir.find_repositoryV2'
360
verb = 'BzrDir.find_repositoryV2'
859
361
response = self._call(verb, path)
860
if response[0] != b'ok':
362
if response[0] != 'ok':
861
363
raise errors.UnexpectedSmartServerResponse(response)
862
364
self._ensure_real()
863
365
repo = self._real_bzrdir.open_repository()
956
441
"""Upgrading of remote bzrdirs is not supported yet."""
959
def needs_format_conversion(self, format):
444
def needs_format_conversion(self, format=None):
960
445
"""Upgrading of remote bzrdirs is not supported yet."""
447
symbol_versioning.warn(symbol_versioning.deprecated_in((1, 13, 0))
448
% 'needs_format_conversion(format=None)')
451
def clone(self, url, revision_id=None, force_new_repo=False,
452
preserve_stacking=False):
454
return self._real_bzrdir.clone(url, revision_id=revision_id,
455
force_new_repo=force_new_repo, preserve_stacking=preserve_stacking)
963
457
def _get_config(self):
964
458
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):
461
class RemoteRepositoryFormat(repository.RepositoryFormat):
1005
462
"""Format for repositories accessed over a _SmartClient.
1007
464
Instances of this repository are represented by RemoteRepository
1094
529
self._custom_format.supports_tree_reference
1095
530
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):
532
def _vfs_initialize(self, a_bzrdir, shared):
1106
533
"""Helper for common code in initialize."""
1107
534
if self._custom_format:
1108
535
# Custom format requested
1109
result = self._custom_format.initialize(
1110
a_controldir, shared=shared)
536
result = self._custom_format.initialize(a_bzrdir, shared=shared)
1111
537
elif self._creating_bzrdir is not None:
1112
538
# Use the format that the repository we were created to back
1114
540
prior_repo = self._creating_bzrdir.open_repository()
1115
541
prior_repo._ensure_real()
1116
542
result = prior_repo._real_repository._format.initialize(
1117
a_controldir, shared=shared)
543
a_bzrdir, shared=shared)
1119
545
# assume that a_bzr is a RemoteBzrDir but the smart server didn't
1120
546
# support remote initialization.
1121
547
# We delegate to a real object at this point (as RemoteBzrDir
1122
548
# 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)
549
# recursion if we just called a_bzrdir.create_repository.
550
a_bzrdir._ensure_real()
551
result = a_bzrdir._real_bzrdir.create_repository(shared=shared)
1126
552
if not isinstance(result, RemoteRepository):
1127
return self.open(a_controldir)
553
return self.open(a_bzrdir)
1131
def initialize(self, a_controldir, shared=False):
557
def initialize(self, a_bzrdir, shared=False):
1132
558
# 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
559
if not isinstance(a_bzrdir, RemoteBzrDir):
560
return self._vfs_initialize(a_bzrdir, shared)
561
medium = a_bzrdir._client._medium
1136
562
if medium._is_remote_before((1, 13)):
1137
return self._vfs_initialize(a_controldir, shared)
563
return self._vfs_initialize(a_bzrdir, shared)
1138
564
# Creating on a remote bzr dir.
1139
565
# 1) get the network name to use.
1140
566
if self._custom_format:
1142
568
elif self._network_name:
1143
569
network_name = self._network_name
1145
# Select the current breezy default and ask for that.
1146
reference_bzrdir_format = controldir.format_registry.get(
571
# Select the current bzrlib default and ask for that.
572
reference_bzrdir_format = bzrdir.format_registry.get('default')()
1148
573
reference_format = reference_bzrdir_format.repository_format
1149
574
network_name = reference_format.network_name()
1150
575
# 2) try direct creation via RPC
1151
path = a_controldir._path_for_remote_call(a_controldir._client)
1152
verb = b'BzrDir.create_repository'
576
path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
577
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)
583
response = a_bzrdir._call(verb, path, network_name, shared_str)
1159
584
except errors.UnknownSmartMethod:
1160
585
# Fallback - use vfs methods
1161
586
medium._remember_remote_is_before((1, 13))
1162
return self._vfs_initialize(a_controldir, shared)
587
return self._vfs_initialize(a_bzrdir, shared)
1164
589
# Turn the response into a RemoteRepository object.
1165
590
format = response_tuple_to_repo_format(response[1:])
1166
591
# Used to support creating a real format instance when needed.
1167
format._creating_bzrdir = a_controldir
1168
remote_repo = RemoteRepository(a_controldir, format)
592
format._creating_bzrdir = a_bzrdir
593
remote_repo = RemoteRepository(a_bzrdir, format)
1169
594
format._creating_repo = remote_repo
1170
595
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()
597
def open(self, a_bzrdir):
598
if not isinstance(a_bzrdir, RemoteBzrDir):
599
raise AssertionError('%r is not a RemoteBzrDir' % (a_bzrdir,))
600
return a_bzrdir.open_repository()
1177
602
def _ensure_real(self):
1178
603
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)
604
self._custom_format = repository.network_format_registry.get(
1187
608
def _fetch_order(self):
1551
909
# TODO: Move to RepositoryBase and unify with the regular Repository
1552
910
# one; unfortunately the tests rely on slightly different behaviour at
1553
911
# present -- mbp 20090710
1554
return (self.__class__ is other.__class__
1555
and self.controldir.transport.base == other.controldir.transport.base)
912
return (self.__class__ is other.__class__ and
913
self.bzrdir.transport.base == other.bzrdir.transport.base)
1557
915
def get_graph(self, other_repository=None):
1558
916
"""Return the graph for this repository format"""
1559
917
parents_provider = self._make_parents_provider(other_repository)
1560
918
return graph.Graph(parents_provider)
1562
921
def get_known_graph_ancestry(self, revision_ids):
1563
922
"""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)
924
st = static_tuple.StaticTuple
925
revision_keys = [st(r_id).intern() for r_id in revision_ids]
926
known_graph = self.revisions.get_known_graph_ancestry(revision_keys)
927
return graph.GraphThunkIdsToKeys(known_graph)
1571
929
def gather_stats(self, revid=None, committers=None):
1572
930
"""See Repository.gather_stats()."""
1573
path = self.controldir._path_for_remote_call(self._client)
931
path = self.bzrdir._path_for_remote_call(self._client)
1574
932
# revid can be None to indicate no revisions, not just NULL_REVISION
1575
if revid is None or _mod_revision.is_null(revid):
933
if revid is None or revision.is_null(revid):
1578
936
fmt_revid = revid
1579
937
if committers is None or not committers:
1580
fmt_committers = b'no'
938
fmt_committers = 'no'
1582
fmt_committers = b'yes'
940
fmt_committers = 'yes'
1583
941
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':
942
'Repository.gather_stats', path, fmt_revid, fmt_committers)
943
if response_tuple[0] != 'ok':
1586
944
raise errors.UnexpectedSmartServerResponse(response_tuple)
1588
946
body = response_handler.read_body_bytes()
1590
for line in body.split(b'\n'):
948
for line in body.split('\n'):
1593
key, val_text = line.split(b':')
1594
key = key.decode('ascii')
951
key, val_text = line.split(':')
1595
952
if key in ('revisions', 'size', 'committers'):
1596
953
result[key] = int(val_text)
1597
954
elif key in ('firstrev', 'latestrev'):
1598
values = val_text.split(b' ')[1:]
1599
result[key] = (float(values[0]), int(values[1]))
955
values = val_text.split(' ')[1:]
956
result[key] = (float(values[0]), long(values[1]))
1880
1195
raise errors.UnexpectedSmartServerResponse(response)
1882
1197
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()
1198
# TODO: Option to control what format is created?
1200
dest_repo = self._real_repository._format.initialize(to_bzrdir,
1202
dest_repo.fetch(self, revision_id=revision_id)
1904
1203
return dest_repo
1906
# These methods are just thin shims to the VFS object for now.
1205
### These methods are just thin shims to the VFS object for now.
1908
1207
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]
1209
return self._real_repository.revision_tree(revision_id)
1917
1211
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)
1213
return self._real_repository.get_serializer_format()
1929
1215
def get_commit_builder(self, branch, parents, config, timestamp=None,
1930
1216
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()
1218
# FIXME: It ought to be possible to call this without immediately
1219
# triggering _ensure_real. For now it's the easiest thing to do.
1221
real_repo = self._real_repository
1222
builder = real_repo.get_commit_builder(branch, parents,
1223
config, timestamp=timestamp, timezone=timezone,
1224
committer=committer, revprops=revprops, revision_id=revision_id)
1956
1227
def add_fallback_repository(self, repository):
1957
1228
"""Add a repository to use for looking up data not held locally.
1996
1266
return self._real_repository.add_inventory(revid, inv, parents)
1998
1268
def add_inventory_by_delta(self, basis_revision_id, delta, new_revision_id,
1999
parents, basis_inv=None, propagate_caches=False):
1269
parents, basis_inv=None, propagate_caches=False):
2000
1270
self._ensure_real()
2001
1271
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)
1272
delta, new_revision_id, parents, basis_inv=basis_inv,
1273
propagate_caches=propagate_caches)
1275
def add_revision(self, rev_id, rev, inv=None, config=None):
1277
return self._real_repository.add_revision(
1278
rev_id, rev, inv=inv, config=config)
2031
1281
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
1282
self._ensure_real()
2080
return self._real_repository._iter_inventories(revision_ids, ordering)
1283
return self._real_repository.get_inventory(revision_id)
2082
1285
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()
1287
return self._real_repository.iter_inventories(revision_ids, ordering)
2158
1290
def get_revision(self, revision_id):
2159
with self.lock_read():
2160
return self.get_revisions([revision_id])[0]
1292
return self._real_repository.get_revision(revision_id)
2162
1294
def get_transaction(self):
2163
1295
self._ensure_real()
2164
1296
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)
1299
def clone(self, a_bzrdir, revision_id=None):
1301
return self._real_repository.clone(a_bzrdir, revision_id=revision_id)
2173
1303
def make_working_trees(self):
2174
1304
"""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'
1306
return self._real_repository.make_working_trees()
2186
1308
def refresh_data(self):
2187
"""Re-read any data needed to synchronise with disk.
1309
"""Re-read any data needed to to synchronise with disk.
2189
1311
This method is intended to be called after another repository instance
2190
1312
(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.
1313
repository. It may not be called during a write group, but may be
1314
called at any other time.
1316
if self.is_in_write_group():
1317
raise errors.InternalBzrError(
1318
"May not refresh_data while in a write group.")
2197
1319
if self._real_repository is not None:
2198
1320
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
1322
def revision_ids_to_search_result(self, result_set):
2204
1323
"""Convert a set of revision ids to a graph SearchResult."""
2205
1324
result_parents = set()
2206
for parents in viewvalues(self.get_graph().get_parent_map(result_set)):
1325
for parents in self.get_graph().get_parent_map(
1326
result_set).itervalues():
2207
1327
result_parents.update(parents)
2208
1328
included_keys = result_set.intersection(result_parents)
2209
1329
start_keys = result_set.difference(included_keys)
2210
1330
exclude_keys = result_parents.difference(result_set)
2211
result = vf_search.SearchResult(start_keys, exclude_keys,
2212
len(result_set), result_set)
1331
result = graph.SearchResult(start_keys, exclude_keys,
1332
len(result_set), result_set)
2215
def search_missing_revision_ids(self, other,
2216
find_ghosts=True, revision_ids=None, if_present_ids=None,
1336
def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
2218
1337
"""Return the revision ids that other has that this does not.
2220
1339
These are returned in topological order.
2222
1341
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)
1343
return repository.InterRepository.get(
1344
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):
1346
def fetch(self, source, revision_id=None, pb=None, find_ghosts=False,
2232
1348
# No base implementation to use as RemoteRepository is not a subclass
2233
1349
# of Repository; so this is a copy of Repository.fetch().
2234
1350
if fetch_spec is not None and revision_id is not None:
2272
1388
return self._real_repository._get_versioned_file_checker(
2273
1389
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
1391
def iter_files_bytes(self, desired_files):
2327
1392
"""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)
1395
return self._real_repository.iter_files_bytes(desired_files)
2359
1397
def get_parent_map(self, revision_ids):
2360
"""See breezy.Graph.get_parent_map()."""
1398
"""See bzrlib.Graph.get_parent_map()."""
2361
1399
return self._make_parents_provider().get_parent_map(revision_ids)
2363
1401
def _get_parent_map_rpc(self, keys):
2480
1529
revision_graph[d[0]] = (NULL_REVISION,)
2481
1530
return revision_graph
2483
1533
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()
1535
return self._real_repository.get_signature_text(revision_id)
2504
1538
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)
1540
return self._real_repository._get_inventory_xml(revision_id)
2511
1542
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)
1544
return self._real_repository.reconcile(other=other, thorough=thorough)
2540
1546
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)
1548
return self._real_repository.all_revision_ids()
2571
1551
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]
1553
return self._real_repository.get_deltas_for_revisions(revisions,
1554
specific_fileids=specific_fileids)
1557
def get_revision_delta(self, revision_id, specific_fileids=None):
1559
return self._real_repository.get_revision_delta(revision_id,
1560
specific_fileids=specific_fileids)
2613
1563
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)
1565
return self._real_repository.revision_trees(revision_ids)
2619
1568
def get_revision_reconcile(self, revision_id):
2620
with self.lock_read():
2622
return self._real_repository.get_revision_reconcile(revision_id)
1570
return self._real_repository.get_revision_reconcile(revision_id)
2624
1573
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)
1575
return self._real_repository.check(revision_ids=revision_ids,
1576
callback_refs=callback_refs, check_repo=check_repo)
2630
1578
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)
1580
return self._real_repository.copy_content_into(
1581
destination, revision_id=revision_id)
2639
1583
def _copy_repository_tarball(self, to_bzrdir, revision_id=None):
2640
1584
# get a tarball of the remote repository, and copy from that into the
1586
from bzrlib import osutils
2643
1588
# TODO: Maybe a progress bar while streaming the tarball?
2644
note(gettext("Copying repository content as tarball..."))
1589
note("Copying repository content as tarball...")
2645
1590
tar_file = self._get_tarball('bz2')
2646
1591
if tar_file is None:
2648
1593
destination = to_bzrdir.create_repository()
2650
1595
tar = tarfile.open('repository', fileobj=tar_file,
2652
1597
tmpdir = osutils.mkdtemp()
2654
tar.extractall(tmpdir)
2655
tmp_bzrdir = _mod_bzrdir.BzrDir.open(tmpdir)
1599
_extract_tar(tar, tmpdir)
1600
tmp_bzrdir = BzrDir.open(tmpdir)
2656
1601
tmp_repo = tmp_bzrdir.open_repository()
2657
1602
tmp_repo.copy_content_into(destination, revision_id)
2746
1680
self._ensure_real()
2747
1681
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):
1684
def get_revisions(self, revision_ids):
1686
return self._real_repository.get_revisions(revision_ids)
2800
1688
def supports_rich_root(self):
2801
1689
return self._format.rich_root_data
1691
def iter_reverse_revision_history(self, revision_id):
1693
return self._real_repository.iter_reverse_revision_history(revision_id)
2804
1696
def _serializer(self):
2805
1697
return self._format._serializer
2807
1699
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)
1701
return self._real_repository.store_revision_signature(
1702
gpg_strategy, plaintext, revision_id)
2812
1704
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:]]
1706
return self._real_repository.add_signature_text(revision_id, signature)
2833
1708
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)
1710
return self._real_repository.has_signature_for_revision_id(revision_id)
2866
1712
def item_keys_introduced_by(self, revision_ids, _files_pb=None):
2867
1713
self._ensure_real()
2868
1714
return self._real_repository.item_keys_introduced_by(revision_ids,
2869
_files_pb=_files_pb)
1715
_files_pb=_files_pb)
1717
def revision_graph_can_have_wrong_parents(self):
1718
# The answer depends on the remote repo format.
1720
return self._real_repository.revision_graph_can_have_wrong_parents()
2871
1722
def _find_inconsistent_revision_parents(self, revisions_iterator=None):
2872
1723
self._ensure_real()
2890
1742
:param recipe: A search recipe (start, stop, count).
2891
1743
: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))
1745
start_keys = ' '.join(recipe[1])
1746
stop_keys = ' '.join(recipe[2])
1747
count = str(recipe[3])
1748
return '\n'.join((start_keys, stop_keys, count))
2898
1750
def _serialise_search_result(self, search_result):
2899
parts = search_result.get_network_struct()
2900
return b'\n'.join(parts)
1751
if isinstance(search_result, graph.PendingAncestryResult):
1752
parts = ['ancestry-of']
1753
parts.extend(search_result.heads)
1755
recipe = search_result.get_recipe()
1756
parts = [recipe[0], self._serialise_search_recipe(recipe)]
1757
return '\n'.join(parts)
2902
1759
def autopack(self):
2903
path = self.controldir._path_for_remote_call(self._client)
1760
path = self.bzrdir._path_for_remote_call(self._client)
2905
response = self._call(b'PackRepository.autopack', path)
1762
response = self._call('PackRepository.autopack', path)
2906
1763
except errors.UnknownSmartMethod:
2907
1764
self._ensure_real()
2908
1765
self._real_repository._pack_collection.autopack()
2910
1767
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):
1768
if response[0] != 'ok':
1769
raise errors.UnexpectedSmartServerResponse(response)
1772
class RemoteStreamSink(repository.StreamSink):
2954
1774
def _insert_real(self, stream, src_format, resume_tokens):
2955
1775
self.target_repo._ensure_real()
3299
2059
def network_name(self):
3300
2060
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)
2062
def open(self, a_bzrdir, name=None, ignore_fallbacks=False):
2063
return a_bzrdir.open_branch(name=name,
2064
ignore_fallbacks=ignore_fallbacks)
3306
def _vfs_initialize(self, a_controldir, name, append_revisions_only,
2066
def _vfs_initialize(self, a_bzrdir, name):
3308
2067
# Initialisation when using a local bzrdir object, or a non-vfs init
3309
2068
# method is not available on the server.
3310
2069
# 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)
2071
if isinstance(a_bzrdir, RemoteBzrDir):
2072
a_bzrdir._ensure_real()
2073
result = self._custom_format.initialize(a_bzrdir._real_bzrdir,
3318
2076
# 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,
2077
result = self._custom_format.initialize(a_bzrdir, name)
2078
if (isinstance(a_bzrdir, RemoteBzrDir) and
2079
not isinstance(result, RemoteBranch)):
2080
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()
2084
def initialize(self, a_bzrdir, name=None):
3332
2085
# 1) get the network name to use.
3333
2086
if self._custom_format:
3334
2087
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(
2089
# Select the current bzrlib default and ask for that.
2090
reference_bzrdir_format = bzrdir.format_registry.get('default')()
3339
2091
reference_format = reference_bzrdir_format.get_branch_format()
3340
2092
self._custom_format = reference_format
3341
2093
network_name = reference_format.network_name()
3342
2094
# 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
2095
if not isinstance(a_bzrdir, RemoteBzrDir):
2096
return self._vfs_initialize(a_bzrdir, name=name)
2097
medium = a_bzrdir._client._medium
3348
2098
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)
2099
return self._vfs_initialize(a_bzrdir, name=name)
3352
2100
# Creating on a remote bzr dir.
3353
2101
# 2) try direct creation via RPC
3354
path = a_controldir._path_for_remote_call(a_controldir._client)
2102
path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
2103
if name is not None:
3356
2104
# XXX JRV20100304: Support creating colocated branches
3357
2105
raise errors.NoColocatedBranchSupport(self)
3358
verb = b'BzrDir.create_branch'
2106
verb = 'BzrDir.create_branch'
3360
response = a_controldir._call(verb, path, network_name)
2108
response = a_bzrdir._call(verb, path, network_name)
3361
2109
except errors.UnknownSmartMethod:
3362
2110
# Fallback - use vfs methods
3363
2111
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':
2112
return self._vfs_initialize(a_bzrdir, name=name)
2113
if response[0] != 'ok':
3368
2114
raise errors.UnexpectedSmartServerResponse(response)
3369
2115
# Turn the response into a RemoteRepository object.
3370
2116
format = RemoteBranchFormat(network_name=response[1])
3371
2117
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
2118
if response[2] == '':
2119
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)
2121
repo_bzrdir = RemoteBzrDir(
2122
a_bzrdir.root_transport.clone(response[2]), a_bzrdir._format,
2124
remote_repo = RemoteRepository(repo_bzrdir, repo_format)
2125
remote_branch = RemoteBranch(a_bzrdir, remote_repo,
2126
format=format, setup_stacking=False, name=name)
3396
2127
# XXX: We know this is a new branch, so it must have revno 0, revid
3397
2128
# NULL_REVISION. Creating the branch locked would make this be unable
3398
2129
# to be wrong; here its simply very unlikely to be wrong. RBC 20090225
3417
2148
self._ensure_real()
3418
2149
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
2152
class RemoteBranch(branch.Branch, _RpcHelper, lock._RelockDebugMixin):
3492
2153
"""Branch stored on a server accessed by HPSS RPC.
4043
2644
self._ensure_real()
4044
2645
return self._real_branch._set_parent_location(url)
4046
2648
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
2650
self._clear_cached_state_of_remote_branch_only()
2652
return self._real_branch.pull(
2653
source, overwrite=overwrite, stop_revision=stop_revision,
2654
_override_hook_target=self, **kwargs)
2657
def push(self, target, overwrite=False, stop_revision=None):
2659
return self._real_branch.push(
2660
target, overwrite=overwrite, stop_revision=stop_revision,
2661
_override_hook_source_branch=self)
4065
2663
def is_locked(self):
4066
2664
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
2667
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)
2669
return self._real_branch.revision_id_to_revno(revision_id)
4112
2672
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)
2673
# XXX: These should be returned by the set_last_revision_info verb
2674
old_revno, old_revid = self.last_revision_info()
2675
self._run_pre_change_branch_tip_hooks(revno, revision_id)
2676
revision_id = ensure_null(revision_id)
2678
response = self._call('Branch.set_last_revision_info',
2679
self._remote_path(), self._lock_token, self._repo_lock_token,
2680
str(revno), revision_id)
2681
except errors.UnknownSmartMethod:
2683
self._clear_cached_state_of_remote_branch_only()
2684
self._real_branch.set_last_revision_info(revno, revision_id)
2685
self._last_revision_info_cache = revno, revision_id
2687
if response == ('ok',):
2688
self._clear_cached_state()
2689
self._last_revision_info_cache = revno, revision_id
2690
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2691
# Update the _real_branch's cache too.
2692
if self._real_branch is not None:
2693
cache = self._last_revision_info_cache
2694
self._real_branch._last_revision_info_cache = cache
2696
raise errors.UnexpectedSmartServerResponse(response)
4141
2699
def generate_revision_history(self, revision_id, last_rev=None,
4142
2700
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)
2701
medium = self._client._medium
2702
if not medium._is_remote_before((1, 6)):
2703
# Use a smart method for 1.6 and above servers
2705
self._set_last_revision_descendant(revision_id, other_branch,
2706
allow_diverged=True, allow_overwrite_descendant=True)
2708
except errors.UnknownSmartMethod:
2709
medium._remember_remote_is_before((1, 6))
2710
self._clear_cached_state_of_remote_branch_only()
2711
self.set_revision_history(self._lefthand_history(revision_id,
2712
last_rev=last_rev,other_branch=other_branch))
4168
2714
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)
2716
return self._real_branch.set_push_location(location)
4261
2719
class RemoteConfig(object):
4326
2774
medium = self._branch._client._medium
4327
2775
if medium._is_remote_before((1, 14)):
4328
2776
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
2778
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'))
2779
response = self._branch._client.call('Branch.set_config_option',
2780
path, self._branch._lock_token, self._branch._repo_lock_token,
2781
value.encode('utf8'), name, section or '')
4349
2782
except errors.UnknownSmartMethod:
4350
medium = self._branch._client._medium
4351
2783
medium._remember_remote_is_before((1, 14))
4352
2784
return self._vfs_set_option(value, name, section)
4353
2785
if response != ():
4354
2786
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
2788
def _real_object(self):
4382
2789
self._branch._ensure_real()
4383
2790
return self._branch._real_branch
4443
2856
def find(name):
4445
2858
return context[name]
4447
mutter('Missing key \'%s\' in context %r', name, context)
2859
except KeyError, key_err:
2860
mutter('Missing key %r in context %r', key_err.args[0], context)
4450
2862
def get_path():
4451
2863
"""Get the path from the context if present, otherwise use first error
4455
2867
return context['path']
2868
except KeyError, key_err:
4458
return err.error_args[0].decode('utf-8')
4460
mutter('Missing key \'path\' in context %r', context)
2870
return err.error_args[0]
2871
except IndexError, idx_err:
2873
'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')))
2876
if err.error_verb == 'IncompatibleRepositories':
2877
raise errors.IncompatibleRepositories(err.error_args[0],
2878
err.error_args[1], err.error_args[2])
2879
elif err.error_verb == 'NoSuchRevision':
2880
raise NoSuchRevision(find('branch'), err.error_args[0])
2881
elif err.error_verb == 'nosuchrevision':
2882
raise NoSuchRevision(find('repository'), err.error_args[0])
2883
elif err.error_verb == 'nobranch':
2884
if len(err.error_args) >= 1:
2885
extra = err.error_args[0]
2888
raise errors.NotBranchError(path=find('bzrdir').root_transport.base,
2890
elif err.error_verb == 'norepository':
2891
raise errors.NoRepositoryPresent(find('bzrdir'))
2892
elif err.error_verb == 'LockContention':
2893
raise errors.LockContention('(remote lock)')
2894
elif err.error_verb == 'UnlockableTransport':
2895
raise errors.UnlockableTransport(find('bzrdir').root_transport)
2896
elif err.error_verb == 'LockFailed':
2897
raise errors.LockFailed(err.error_args[0], err.error_args[1])
2898
elif err.error_verb == 'TokenMismatch':
2899
raise errors.TokenMismatch(find('token'), '(remote token)')
2900
elif err.error_verb == 'Diverged':
2901
raise errors.DivergedBranches(find('branch'), find('other_branch'))
2902
elif err.error_verb == 'TipChangeRejected':
2903
raise errors.TipChangeRejected(err.error_args[0].decode('utf8'))
2904
elif err.error_verb == 'UnstackableBranchFormat':
2905
raise errors.UnstackableBranchFormat(*err.error_args)
2906
elif err.error_verb == 'UnstackableRepositoryFormat':
2907
raise errors.UnstackableRepositoryFormat(*err.error_args)
2908
elif err.error_verb == 'NotStacked':
2909
raise errors.NotStacked(branch=find('branch'))
2910
elif err.error_verb == 'PermissionDenied':
2912
if len(err.error_args) >= 2:
2913
extra = err.error_args[1]
2916
raise errors.PermissionDenied(path, extra=extra)
2917
elif err.error_verb == 'ReadError':
2919
raise errors.ReadError(path)
2920
elif err.error_verb == 'NoSuchFile':
2922
raise errors.NoSuchFile(path)
2923
elif err.error_verb == 'FileExists':
2924
raise errors.FileExists(err.error_args[0])
2925
elif err.error_verb == 'DirectoryNotEmpty':
2926
raise errors.DirectoryNotEmpty(err.error_args[0])
2927
elif err.error_verb == 'ShortReadvError':
2928
args = err.error_args
2929
raise errors.ShortReadvError(
2930
args[0], int(args[1]), int(args[2]), int(args[3]))
2931
elif err.error_verb in ('UnicodeEncodeError', 'UnicodeDecodeError'):
2932
encoding = str(err.error_args[0]) # encoding must always be a string
2933
val = err.error_args[1]
2934
start = int(err.error_args[2])
2935
end = int(err.error_args[3])
2936
reason = str(err.error_args[4]) # reason must always be a string
2937
if val.startswith('u:'):
2938
val = val[2:].decode('utf-8')
2939
elif val.startswith('s:'):
2940
val = val[2:].decode('base64')
2941
if err.error_verb == 'UnicodeDecodeError':
2942
raise UnicodeDecodeError(encoding, val, start, end, reason)
2943
elif err.error_verb == 'UnicodeEncodeError':
2944
raise UnicodeEncodeError(encoding, val, start, end, reason)
2945
elif err.error_verb == 'ReadOnlyError':
2946
raise errors.TransportNotPossible('readonly transport')
2947
raise errors.UnknownErrorFromSmartServer(err)