73
96
return self._client.call_with_body_bytes_expecting_body(
74
97
method, args, body_bytes)
75
except errors.ErrorFromSmartServer, err:
98
except errors.ErrorFromSmartServer as err:
76
99
self._translate_error(err, **err_context)
79
102
def response_tuple_to_repo_format(response):
80
103
"""Convert a response tuple describing a repository format to a format."""
81
104
format = RemoteRepositoryFormat()
82
format._rich_root_data = (response[0] == 'yes')
83
format._supports_tree_reference = (response[1] == 'yes')
84
format._supports_external_lookups = (response[2] == 'yes')
105
format._rich_root_data = (response[0] == b'yes')
106
format._supports_tree_reference = (response[1] == b'yes')
107
format._supports_external_lookups = (response[2] == b'yes')
85
108
format._network_name = response[3]
89
# Note: RemoteBzrDirFormat is in bzrdir.py
91
class RemoteBzrDir(BzrDir, _RpcHelper):
112
# Note that RemoteBzrDirProber lives in breezy.bzrdir so breezy.bzr.remote
113
# does not have to be imported unless a remote format is involved.
115
class RemoteBzrDirFormat(_mod_bzrdir.BzrDirMetaFormat1):
116
"""Format representing bzrdirs accessed via a smart server"""
118
supports_workingtrees = False
120
colocated_branches = False
123
_mod_bzrdir.BzrDirMetaFormat1.__init__(self)
124
# XXX: It's a bit ugly that the network name is here, because we'd
125
# like to believe that format objects are stateless or at least
126
# immutable, However, we do at least avoid mutating the name after
127
# it's returned. See <https://bugs.launchpad.net/bzr/+bug/504102>
128
self._network_name = None
131
return "%s(_network_name=%r)" % (self.__class__.__name__,
134
def get_format_description(self):
135
if self._network_name:
137
real_format = controldir.network_format_registry.get(
142
return 'Remote: ' + real_format.get_format_description()
143
return 'bzr remote bzrdir'
145
def get_format_string(self):
146
raise NotImplementedError(self.get_format_string)
148
def network_name(self):
149
if self._network_name:
150
return self._network_name
152
raise AssertionError("No network name set.")
154
def initialize_on_transport(self, transport):
156
# hand off the request to the smart server
157
client_medium = transport.get_smart_medium()
158
except errors.NoSmartMedium:
159
# TODO: lookup the local format from a server hint.
160
local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
161
return local_dir_format.initialize_on_transport(transport)
162
client = _SmartClient(client_medium)
163
path = client.remote_path_from_transport(transport)
165
response = client.call(b'BzrDirFormat.initialize', path)
166
except errors.ErrorFromSmartServer as err:
167
_translate_error(err, path=path)
168
if response[0] != b'ok':
169
raise errors.SmartProtocolError(
170
'unexpected response code %s' % (response,))
171
format = RemoteBzrDirFormat()
172
self._supply_sub_formats_to(format)
173
return RemoteBzrDir(transport, format)
175
def parse_NoneTrueFalse(self, arg):
182
raise AssertionError("invalid arg %r" % arg)
184
def _serialize_NoneTrueFalse(self, arg):
191
def _serialize_NoneString(self, arg):
194
def initialize_on_transport_ex(self, transport, use_existing_dir=False,
195
create_prefix=False, force_new_repo=False, stacked_on=None,
196
stack_on_pwd=None, repo_format_name=None, make_working_trees=None,
199
# hand off the request to the smart server
200
client_medium = transport.get_smart_medium()
201
except errors.NoSmartMedium:
204
# Decline to open it if the server doesn't support our required
205
# version (3) so that the VFS-based transport will do it.
206
if client_medium.should_probe():
208
server_version = client_medium.protocol_version()
209
if server_version != '2':
213
except errors.SmartProtocolError:
214
# Apparently there's no usable smart server there, even though
215
# the medium supports the smart protocol.
220
client = _SmartClient(client_medium)
221
path = client.remote_path_from_transport(transport)
222
if client_medium._is_remote_before((1, 16)):
225
# TODO: lookup the local format from a server hint.
226
local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
227
self._supply_sub_formats_to(local_dir_format)
228
return local_dir_format.initialize_on_transport_ex(transport,
229
use_existing_dir=use_existing_dir, create_prefix=create_prefix,
230
force_new_repo=force_new_repo, stacked_on=stacked_on,
231
stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
232
make_working_trees=make_working_trees, shared_repo=shared_repo,
234
return self._initialize_on_transport_ex_rpc(client, path, transport,
235
use_existing_dir, create_prefix, force_new_repo, stacked_on,
236
stack_on_pwd, repo_format_name, make_working_trees, shared_repo)
238
def _initialize_on_transport_ex_rpc(self, client, path, transport,
239
use_existing_dir, create_prefix, force_new_repo, stacked_on,
240
stack_on_pwd, repo_format_name, make_working_trees, shared_repo):
242
args.append(self._serialize_NoneTrueFalse(use_existing_dir))
243
args.append(self._serialize_NoneTrueFalse(create_prefix))
244
args.append(self._serialize_NoneTrueFalse(force_new_repo))
245
args.append(self._serialize_NoneString(stacked_on))
246
# stack_on_pwd is often/usually our transport
249
stack_on_pwd = transport.relpath(stack_on_pwd).encode('utf-8')
252
except errors.PathNotChild:
254
args.append(self._serialize_NoneString(stack_on_pwd))
255
args.append(self._serialize_NoneString(repo_format_name))
256
args.append(self._serialize_NoneTrueFalse(make_working_trees))
257
args.append(self._serialize_NoneTrueFalse(shared_repo))
258
request_network_name = self._network_name or \
259
_mod_bzrdir.BzrDirFormat.get_default_format().network_name()
261
response = client.call(b'BzrDirFormat.initialize_ex_1.16',
262
request_network_name, path, *args)
263
except errors.UnknownSmartMethod:
264
client._medium._remember_remote_is_before((1, 16))
265
local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
266
self._supply_sub_formats_to(local_dir_format)
267
return local_dir_format.initialize_on_transport_ex(transport,
268
use_existing_dir=use_existing_dir, create_prefix=create_prefix,
269
force_new_repo=force_new_repo, stacked_on=stacked_on,
270
stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
271
make_working_trees=make_working_trees, shared_repo=shared_repo,
273
except errors.ErrorFromSmartServer as err:
274
_translate_error(err, path=path.decode('utf-8'))
275
repo_path = response[0]
276
bzrdir_name = response[6]
277
require_stacking = response[7]
278
require_stacking = self.parse_NoneTrueFalse(require_stacking)
279
format = RemoteBzrDirFormat()
280
format._network_name = bzrdir_name
281
self._supply_sub_formats_to(format)
282
bzrdir = RemoteBzrDir(transport, format, _client=client)
284
repo_format = response_tuple_to_repo_format(response[1:])
285
if repo_path == b'.':
287
repo_path = repo_path.decode('utf-8')
289
repo_bzrdir_format = RemoteBzrDirFormat()
290
repo_bzrdir_format._network_name = response[5]
291
repo_bzr = RemoteBzrDir(transport.clone(repo_path),
295
final_stack = response[8] or None
297
final_stack = final_stack.decode('utf-8')
298
final_stack_pwd = response[9] or None
300
final_stack_pwd = urlutils.join(
301
transport.base, final_stack_pwd.decode('utf-8'))
302
remote_repo = RemoteRepository(repo_bzr, repo_format)
303
if len(response) > 10:
304
# Updated server verb that locks remotely.
305
repo_lock_token = response[10] or None
306
remote_repo.lock_write(repo_lock_token, _skip_rpc=True)
308
remote_repo.dont_leave_lock_in_place()
310
remote_repo.lock_write()
311
policy = _mod_bzrdir.UseExistingRepository(remote_repo,
312
final_stack, final_stack_pwd, require_stacking)
313
policy.acquire_repository()
317
bzrdir._format.set_branch_format(self.get_branch_format())
319
# The repo has already been created, but we need to make sure that
320
# we'll make a stackable branch.
321
bzrdir._format.require_stacking(_skip_repo=True)
322
return remote_repo, bzrdir, require_stacking, policy
324
def _open(self, transport):
325
return RemoteBzrDir(transport, self)
327
def __eq__(self, other):
328
if not isinstance(other, RemoteBzrDirFormat):
330
return self.get_format_description() == other.get_format_description()
332
def __return_repository_format(self):
333
# Always return a RemoteRepositoryFormat object, but if a specific bzr
334
# repository format has been asked for, tell the RemoteRepositoryFormat
335
# that it should use that for init() etc.
336
result = RemoteRepositoryFormat()
337
custom_format = getattr(self, '_repository_format', None)
339
if isinstance(custom_format, RemoteRepositoryFormat):
342
# We will use the custom format to create repositories over the
343
# wire; expose its details like rich_root_data for code to
345
result._custom_format = custom_format
348
def get_branch_format(self):
349
result = _mod_bzrdir.BzrDirMetaFormat1.get_branch_format(self)
350
if not isinstance(result, RemoteBranchFormat):
351
new_result = RemoteBranchFormat()
352
new_result._custom_format = result
354
self.set_branch_format(new_result)
358
repository_format = property(__return_repository_format,
359
_mod_bzrdir.BzrDirMetaFormat1._set_repository_format) # .im_func)
362
class RemoteControlStore(_mod_config.IniFileStore):
363
"""Control store which attempts to use HPSS calls to retrieve control store.
365
Note that this is specific to bzr-based formats.
368
def __init__(self, bzrdir):
369
super(RemoteControlStore, self).__init__()
370
self.controldir = bzrdir
371
self._real_store = None
373
def lock_write(self, token=None):
375
return self._real_store.lock_write(token)
379
return self._real_store.unlock()
382
with self.lock_write():
383
# We need to be able to override the undecorated implementation
384
self.save_without_locking()
386
def save_without_locking(self):
387
super(RemoteControlStore, self).save()
389
def _ensure_real(self):
390
self.controldir._ensure_real()
391
if self._real_store is None:
392
self._real_store = _mod_config.ControlStore(self.controldir)
394
def external_url(self):
395
return urlutils.join(self.branch.user_url, 'control.conf')
397
def _load_content(self):
398
medium = self.controldir._client._medium
399
path = self.controldir._path_for_remote_call(self.controldir._client)
401
response, handler = self.controldir._call_expecting_body(
402
b'BzrDir.get_config_file', path)
403
except errors.UnknownSmartMethod:
405
return self._real_store._load_content()
406
if len(response) and response[0] != b'ok':
407
raise errors.UnexpectedSmartServerResponse(response)
408
return handler.read_body_bytes()
410
def _save_content(self, content):
411
# FIXME JRV 2011-11-22: Ideally this should use a
412
# HPSS call too, but at the moment it is not possible
413
# to write lock control directories.
415
return self._real_store._save_content(content)
418
class RemoteBzrDir(_mod_bzrdir.BzrDir, _RpcHelper):
92
419
"""Control directory on a remote server, accessed via bzr:// or similar."""
94
421
def __init__(self, transport, format, _client=None, _force_probe=False):
186
555
medium = self._client._medium
187
556
if medium._is_remote_before((1, 13)):
188
557
return self._vfs_cloning_metadir(require_stacking=require_stacking)
189
verb = 'BzrDir.cloning_metadir'
558
verb = b'BzrDir.cloning_metadir'
190
559
if require_stacking:
194
563
path = self._path_for_remote_call(self._client)
196
565
response = self._call(verb, path, stacking)
197
566
except errors.UnknownSmartMethod:
198
567
medium._remember_remote_is_before((1, 13))
199
568
return self._vfs_cloning_metadir(require_stacking=require_stacking)
200
except errors.UnknownErrorFromSmartServer, err:
201
if err.error_tuple != ('BranchReference',):
569
except errors.UnknownErrorFromSmartServer as err:
570
if err.error_tuple != (b'BranchReference',):
203
572
# We need to resolve the branch reference to determine the
204
573
# cloning_metadir. This causes unnecessary RPCs to open the
205
574
# referenced branch (and bzrdir, etc) but only when the caller
206
575
# didn't already resolve the branch reference.
207
576
referenced_branch = self.open_branch()
208
return referenced_branch.bzrdir.cloning_metadir()
577
return referenced_branch.controldir.cloning_metadir()
209
578
if len(response) != 3:
210
579
raise errors.UnexpectedSmartServerResponse(response)
211
580
control_name, repo_name, branch_info = response
212
581
if len(branch_info) != 2:
213
582
raise errors.UnexpectedSmartServerResponse(response)
214
583
branch_ref, branch_name = branch_info
215
format = bzrdir.network_format_registry.get(control_name)
585
format = controldir.network_format_registry.get(control_name)
587
raise errors.UnknownFormatError(
588
kind='control', format=control_name)
217
format.repository_format = repository.network_format_registry.get(
219
if branch_ref == 'ref':
592
format.repository_format = _mod_repository.network_format_registry.get(
595
raise errors.UnknownFormatError(kind='repository',
597
if branch_ref == b'ref':
220
598
# XXX: we need possible_transports here to avoid reopening the
221
599
# connection to the referenced location
222
ref_bzrdir = BzrDir.open(branch_name)
600
ref_bzrdir = _mod_bzrdir.BzrDir.open(branch_name)
223
601
branch_format = ref_bzrdir.cloning_metadir().get_branch_format()
224
602
format.set_branch_format(branch_format)
225
elif branch_ref == 'branch':
603
elif branch_ref == b'branch':
227
format.set_branch_format(
228
branch.network_format_registry.get(branch_name))
606
branch_format = branch.network_format_registry.get(
609
raise errors.UnknownFormatError(kind='branch',
611
format.set_branch_format(branch_format)
230
613
raise errors.UnexpectedSmartServerResponse(response)
266
666
def destroy_branch(self, name=None):
267
667
"""See BzrDir.destroy_branch"""
269
self._real_bzrdir.destroy_branch(name=name)
669
name = self._get_selected_branch()
671
raise errors.NoColocatedBranchSupport(self)
672
path = self._path_for_remote_call(self._client)
678
response = self._call(b'BzrDir.destroy_branch', path, *args)
679
except errors.UnknownSmartMethod:
681
self._real_bzrdir.destroy_branch(name=name)
682
self._next_open_branch_result = None
270
684
self._next_open_branch_result = None
685
if response[0] != b'ok':
686
raise SmartProtocolError(
687
'unexpected response code %s' % (response,))
272
def create_workingtree(self, revision_id=None, from_branch=None):
689
def create_workingtree(self, revision_id=None, from_branch=None,
690
accelerator_tree=None, hardlink=False):
273
691
raise errors.NotLocalUrl(self.transport.base)
275
def find_branch_format(self):
693
def find_branch_format(self, name=None):
276
694
"""Find the branch 'format' for this bzrdir.
278
696
This might be a synthetic object for e.g. RemoteBranch and SVN.
280
b = self.open_branch()
698
b = self.open_branch(name=name)
283
def get_branch_reference(self):
701
def branch_names(self):
702
path = self._path_for_remote_call(self._client)
704
response, handler = self._call_expecting_body(
705
b'BzrDir.get_branches', path)
706
except errors.UnknownSmartMethod:
708
return self._real_bzrdir.branch_names()
709
if response[0] != b"success":
710
raise errors.UnexpectedSmartServerResponse(response)
711
body = bencode.bdecode(handler.read_body_bytes())
713
for name, value in body.items():
714
name = name.decode('utf-8')
718
def get_branches(self, possible_transports=None, ignore_fallbacks=False):
719
path = self._path_for_remote_call(self._client)
721
response, handler = self._call_expecting_body(
722
b'BzrDir.get_branches', path)
723
except errors.UnknownSmartMethod:
725
return self._real_bzrdir.get_branches()
726
if response[0] != b"success":
727
raise errors.UnexpectedSmartServerResponse(response)
728
body = bencode.bdecode(handler.read_body_bytes())
730
for name, value in body.items():
731
name = name.decode('utf-8')
732
ret[name] = self._open_branch(
733
name, value[0].decode('ascii'), value[1],
734
possible_transports=possible_transports,
735
ignore_fallbacks=ignore_fallbacks)
738
def set_branch_reference(self, target_branch, name=None):
739
"""See BzrDir.set_branch_reference()."""
741
name = self._get_selected_branch()
743
raise errors.NoColocatedBranchSupport(self)
745
return self._real_bzrdir.set_branch_reference(target_branch, name=name)
747
def get_branch_reference(self, name=None):
284
748
"""See BzrDir.get_branch_reference()."""
750
name = self._get_selected_branch()
752
raise errors.NoColocatedBranchSupport(self)
285
753
response = self._get_branch_reference()
286
754
if response[0] == 'ref':
755
return response[1].decode('utf-8')
291
759
def _get_branch_reference(self):
760
"""Get branch reference information
762
:return: Tuple with (kind, location_or_format)
763
if kind == 'ref', then location_or_format contains a location
764
otherwise, it contains a format name
292
766
path = self._path_for_remote_call(self._client)
293
767
medium = self._client._medium
294
768
candidate_calls = [
295
('BzrDir.open_branchV3', (2, 1)),
296
('BzrDir.open_branchV2', (1, 13)),
297
('BzrDir.open_branch', None),
769
(b'BzrDir.open_branchV3', (2, 1)),
770
(b'BzrDir.open_branchV2', (1, 13)),
771
(b'BzrDir.open_branch', None),
299
773
for verb, required_version in candidate_calls:
300
774
if required_version and medium._is_remote_before(required_version):
307
781
medium._remember_remote_is_before(required_version)
310
if verb == 'BzrDir.open_branch':
311
if response[0] != 'ok':
784
if verb == b'BzrDir.open_branch':
785
if response[0] != b'ok':
312
786
raise errors.UnexpectedSmartServerResponse(response)
313
if response[1] != '':
787
if response[1] != b'':
314
788
return ('ref', response[1])
316
return ('branch', '')
317
if response[0] not in ('ref', 'branch'):
790
return ('branch', b'')
791
if response[0] not in (b'ref', b'branch'):
318
792
raise errors.UnexpectedSmartServerResponse(response)
793
return (response[0].decode('ascii'), response[1])
321
def _get_tree_branch(self):
795
def _get_tree_branch(self, name=None):
322
796
"""See BzrDir._get_tree_branch()."""
323
return None, self.open_branch()
797
return None, self.open_branch(name=name)
325
def open_branch(self, name=None, unsupported=False,
326
ignore_fallbacks=False):
328
raise NotImplementedError('unsupported flag support not implemented yet.')
329
if self._next_open_branch_result is not None:
330
# See create_branch for details.
331
result = self._next_open_branch_result
332
self._next_open_branch_result = None
334
response = self._get_branch_reference()
335
if response[0] == 'ref':
799
def _open_branch(self, name, kind, location_or_format,
800
ignore_fallbacks=False, possible_transports=None):
336
802
# a branch reference, use the existing BranchReference logic.
337
803
format = BranchReferenceFormat()
804
ref_loc = urlutils.join(self.user_url, location_or_format.decode('utf-8'))
338
805
return format.open(self, name=name, _found=True,
339
location=response[1], ignore_fallbacks=ignore_fallbacks)
340
branch_format_name = response[1]
807
ignore_fallbacks=ignore_fallbacks,
808
possible_transports=possible_transports)
809
branch_format_name = location_or_format
341
810
if not branch_format_name:
342
811
branch_format_name = None
343
812
format = RemoteBranchFormat(network_name=branch_format_name)
344
813
return RemoteBranch(self, self.find_repository(), format=format,
345
setup_stacking=not ignore_fallbacks, name=name)
814
setup_stacking=not ignore_fallbacks, name=name,
815
possible_transports=possible_transports)
817
def open_branch(self, name=None, unsupported=False,
818
ignore_fallbacks=False, possible_transports=None):
820
name = self._get_selected_branch()
822
raise errors.NoColocatedBranchSupport(self)
824
raise NotImplementedError(
825
'unsupported flag support not implemented yet.')
826
if self._next_open_branch_result is not None:
827
# See create_branch for details.
828
result = self._next_open_branch_result
829
self._next_open_branch_result = None
831
response = self._get_branch_reference()
832
return self._open_branch(name, response[0], response[1],
833
possible_transports=possible_transports,
834
ignore_fallbacks=ignore_fallbacks)
347
836
def _open_repo_v1(self, path):
348
verb = 'BzrDir.find_repository'
837
verb = b'BzrDir.find_repository'
349
838
response = self._call(verb, path)
350
if response[0] != 'ok':
839
if response[0] != b'ok':
351
840
raise errors.UnexpectedSmartServerResponse(response)
352
841
# servers that only support the v1 method don't support external
353
842
# references either.
354
843
self._ensure_real()
355
844
repo = self._real_bzrdir.open_repository()
356
response = response + ('no', repo._format.network_name())
845
response = response + (b'no', repo._format.network_name())
357
846
return response, repo
359
848
def _open_repo_v2(self, path):
360
verb = 'BzrDir.find_repositoryV2'
849
verb = b'BzrDir.find_repositoryV2'
361
850
response = self._call(verb, path)
362
if response[0] != 'ok':
851
if response[0] != b'ok':
363
852
raise errors.UnexpectedSmartServerResponse(response)
364
853
self._ensure_real()
365
854
repo = self._real_bzrdir.open_repository()
441
945
"""Upgrading of remote bzrdirs is not supported yet."""
444
def needs_format_conversion(self, format=None):
948
def needs_format_conversion(self, format):
445
949
"""Upgrading of remote bzrdirs is not supported yet."""
447
symbol_versioning.warn(symbol_versioning.deprecated_in((1, 13, 0))
448
% 'needs_format_conversion(format=None)')
451
def clone(self, url, revision_id=None, force_new_repo=False,
452
preserve_stacking=False):
454
return self._real_bzrdir.clone(url, revision_id=revision_id,
455
force_new_repo=force_new_repo, preserve_stacking=preserve_stacking)
457
952
def _get_config(self):
458
953
return RemoteBzrDirConfig(self)
461
class RemoteRepositoryFormat(repository.RepositoryFormat):
955
def _get_config_store(self):
956
return RemoteControlStore(self)
959
class RemoteInventoryTree(InventoryRevisionTree):
961
def __init__(self, repository, inv, revision_id):
962
super(RemoteInventoryTree, self).__init__(repository, inv, revision_id)
964
def archive(self, format, name, root=None, subdir=None, force_mtime=None):
965
ret = self._repository._revision_archive(
966
self.get_revision_id(), format, name, root, subdir,
967
force_mtime=force_mtime)
969
return super(RemoteInventoryTree, self).archive(
970
format, name, root, subdir, force_mtime=force_mtime)
973
def annotate_iter(self, path,
974
default_revision=_mod_revision.CURRENT_REVISION):
975
"""Return an iterator of revision_id, line tuples.
977
For working trees (and mutable trees in general), the special
978
revision_id 'current:' will be used for lines that are new in this
979
tree, e.g. uncommitted changes.
980
:param default_revision: For lines that don't match a basis, mark them
981
with this revision id. Not all implementations will make use of
984
ret = self._repository._annotate_file_revision(
985
self.get_revision_id(), path, file_id=None,
986
default_revision=default_revision)
988
return super(RemoteInventoryTree, self).annotate_iter(
989
path, default_revision=default_revision)
993
class RemoteRepositoryFormat(vf_repository.VersionedFileRepositoryFormat):
462
994
"""Format for repositories accessed over a _SmartClient.
464
996
Instances of this repository are represented by RemoteRepository
529
1083
self._custom_format.supports_tree_reference
530
1084
return self._supports_tree_reference
532
def _vfs_initialize(self, a_bzrdir, shared):
1087
def revision_graph_can_have_wrong_parents(self):
1088
if self._revision_graph_can_have_wrong_parents is None:
1090
self._revision_graph_can_have_wrong_parents = \
1091
self._custom_format.revision_graph_can_have_wrong_parents
1092
return self._revision_graph_can_have_wrong_parents
1094
def _vfs_initialize(self, a_controldir, shared):
533
1095
"""Helper for common code in initialize."""
534
1096
if self._custom_format:
535
1097
# Custom format requested
536
result = self._custom_format.initialize(a_bzrdir, shared=shared)
1098
result = self._custom_format.initialize(
1099
a_controldir, shared=shared)
537
1100
elif self._creating_bzrdir is not None:
538
1101
# Use the format that the repository we were created to back
540
1103
prior_repo = self._creating_bzrdir.open_repository()
541
1104
prior_repo._ensure_real()
542
1105
result = prior_repo._real_repository._format.initialize(
543
a_bzrdir, shared=shared)
1106
a_controldir, shared=shared)
545
1108
# assume that a_bzr is a RemoteBzrDir but the smart server didn't
546
1109
# support remote initialization.
547
1110
# We delegate to a real object at this point (as RemoteBzrDir
548
1111
# delegate to the repository format which would lead to infinite
549
# recursion if we just called a_bzrdir.create_repository.
550
a_bzrdir._ensure_real()
551
result = a_bzrdir._real_bzrdir.create_repository(shared=shared)
1112
# recursion if we just called a_controldir.create_repository.
1113
a_controldir._ensure_real()
1114
result = a_controldir._real_bzrdir.create_repository(shared=shared)
552
1115
if not isinstance(result, RemoteRepository):
553
return self.open(a_bzrdir)
1116
return self.open(a_controldir)
557
def initialize(self, a_bzrdir, shared=False):
1120
def initialize(self, a_controldir, shared=False):
558
1121
# Being asked to create on a non RemoteBzrDir:
559
if not isinstance(a_bzrdir, RemoteBzrDir):
560
return self._vfs_initialize(a_bzrdir, shared)
561
medium = a_bzrdir._client._medium
1122
if not isinstance(a_controldir, RemoteBzrDir):
1123
return self._vfs_initialize(a_controldir, shared)
1124
medium = a_controldir._client._medium
562
1125
if medium._is_remote_before((1, 13)):
563
return self._vfs_initialize(a_bzrdir, shared)
1126
return self._vfs_initialize(a_controldir, shared)
564
1127
# Creating on a remote bzr dir.
565
1128
# 1) get the network name to use.
566
1129
if self._custom_format:
568
1131
elif self._network_name:
569
1132
network_name = self._network_name
571
# Select the current bzrlib default and ask for that.
572
reference_bzrdir_format = bzrdir.format_registry.get('default')()
1134
# Select the current breezy default and ask for that.
1135
reference_bzrdir_format = controldir.format_registry.get(
573
1137
reference_format = reference_bzrdir_format.repository_format
574
1138
network_name = reference_format.network_name()
575
1139
# 2) try direct creation via RPC
576
path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
577
verb = 'BzrDir.create_repository'
1140
path = a_controldir._path_for_remote_call(a_controldir._client)
1141
verb = b'BzrDir.create_repository'
1143
shared_str = b'True'
1145
shared_str = b'False'
583
response = a_bzrdir._call(verb, path, network_name, shared_str)
1147
response = a_controldir._call(verb, path, network_name, shared_str)
584
1148
except errors.UnknownSmartMethod:
585
1149
# Fallback - use vfs methods
586
1150
medium._remember_remote_is_before((1, 13))
587
return self._vfs_initialize(a_bzrdir, shared)
1151
return self._vfs_initialize(a_controldir, shared)
589
1153
# Turn the response into a RemoteRepository object.
590
1154
format = response_tuple_to_repo_format(response[1:])
591
1155
# Used to support creating a real format instance when needed.
592
format._creating_bzrdir = a_bzrdir
593
remote_repo = RemoteRepository(a_bzrdir, format)
1156
format._creating_bzrdir = a_controldir
1157
remote_repo = RemoteRepository(a_controldir, format)
594
1158
format._creating_repo = remote_repo
595
1159
return remote_repo
597
def open(self, a_bzrdir):
598
if not isinstance(a_bzrdir, RemoteBzrDir):
599
raise AssertionError('%r is not a RemoteBzrDir' % (a_bzrdir,))
600
return a_bzrdir.open_repository()
1161
def open(self, a_controldir):
1162
if not isinstance(a_controldir, RemoteBzrDir):
1163
raise AssertionError('%r is not a RemoteBzrDir' % (a_controldir,))
1164
return a_controldir.open_repository()
602
1166
def _ensure_real(self):
603
1167
if self._custom_format is None:
604
self._custom_format = repository.network_format_registry.get(
1169
self._custom_format = _mod_repository.network_format_registry.get(
1172
raise errors.UnknownFormatError(kind='repository',
1173
format=self._network_name)
608
1176
def _fetch_order(self):
909
1540
# TODO: Move to RepositoryBase and unify with the regular Repository
910
1541
# one; unfortunately the tests rely on slightly different behaviour at
911
1542
# present -- mbp 20090710
912
return (self.__class__ is other.__class__ and
913
self.bzrdir.transport.base == other.bzrdir.transport.base)
1543
return (self.__class__ is other.__class__
1544
and self.controldir.transport.base == other.controldir.transport.base)
915
1546
def get_graph(self, other_repository=None):
916
1547
"""Return the graph for this repository format"""
917
1548
parents_provider = self._make_parents_provider(other_repository)
918
1549
return graph.Graph(parents_provider)
921
1551
def get_known_graph_ancestry(self, revision_ids):
922
1552
"""Return the known graph for a set of revision ids and their ancestors.
924
st = static_tuple.StaticTuple
925
revision_keys = [st(r_id).intern() for r_id in revision_ids]
926
known_graph = self.revisions.get_known_graph_ancestry(revision_keys)
927
return graph.GraphThunkIdsToKeys(known_graph)
1554
with self.lock_read():
1555
revision_graph = dict(((key, value) for key, value in
1556
self.get_graph().iter_ancestry(revision_ids) if value is not None))
1557
revision_graph = _mod_repository._strip_NULL_ghosts(revision_graph)
1558
return graph.KnownGraph(revision_graph)
929
1560
def gather_stats(self, revid=None, committers=None):
930
1561
"""See Repository.gather_stats()."""
931
path = self.bzrdir._path_for_remote_call(self._client)
1562
path = self.controldir._path_for_remote_call(self._client)
932
1563
# revid can be None to indicate no revisions, not just NULL_REVISION
933
if revid is None or revision.is_null(revid):
1564
if revid is None or _mod_revision.is_null(revid):
936
1567
fmt_revid = revid
937
1568
if committers is None or not committers:
938
fmt_committers = 'no'
1569
fmt_committers = b'no'
940
fmt_committers = 'yes'
1571
fmt_committers = b'yes'
941
1572
response_tuple, response_handler = self._call_expecting_body(
942
'Repository.gather_stats', path, fmt_revid, fmt_committers)
943
if response_tuple[0] != 'ok':
1573
b'Repository.gather_stats', path, fmt_revid, fmt_committers)
1574
if response_tuple[0] != b'ok':
944
1575
raise errors.UnexpectedSmartServerResponse(response_tuple)
946
1577
body = response_handler.read_body_bytes()
948
for line in body.split('\n'):
1579
for line in body.split(b'\n'):
951
key, val_text = line.split(':')
1582
key, val_text = line.split(b':')
1583
key = key.decode('ascii')
952
1584
if key in ('revisions', 'size', 'committers'):
953
1585
result[key] = int(val_text)
954
1586
elif key in ('firstrev', 'latestrev'):
955
values = val_text.split(' ')[1:]
956
result[key] = (float(values[0]), long(values[1]))
1587
values = val_text.split(b' ')[1:]
1588
result[key] = (float(values[0]), int(values[1]))
1195
1869
raise errors.UnexpectedSmartServerResponse(response)
1197
1871
def sprout(self, to_bzrdir, revision_id=None):
1198
# TODO: Option to control what format is created?
1200
dest_repo = self._real_repository._format.initialize(to_bzrdir,
1202
dest_repo.fetch(self, revision_id=revision_id)
1872
"""Create a descendent repository for new development.
1874
Unlike clone, this does not copy the settings of the repository.
1876
with self.lock_read():
1877
dest_repo = self._create_sprouting_repo(to_bzrdir, shared=False)
1878
dest_repo.fetch(self, revision_id=revision_id)
1881
def _create_sprouting_repo(self, a_controldir, shared):
1882
if not isinstance(a_controldir._format, self.controldir._format.__class__):
1883
# use target default format.
1884
dest_repo = a_controldir.create_repository()
1886
# Most control formats need the repository to be specifically
1887
# created, but on some old all-in-one formats it's not needed
1889
dest_repo = self._format.initialize(
1890
a_controldir, shared=shared)
1891
except errors.UninitializableFormat:
1892
dest_repo = a_controldir.open_repository()
1203
1893
return dest_repo
1205
### These methods are just thin shims to the VFS object for now.
1895
# These methods are just thin shims to the VFS object for now.
1207
1897
def revision_tree(self, revision_id):
1209
return self._real_repository.revision_tree(revision_id)
1898
with self.lock_read():
1899
revision_id = _mod_revision.ensure_null(revision_id)
1900
if revision_id == _mod_revision.NULL_REVISION:
1901
return InventoryRevisionTree(self,
1902
Inventory(root_id=None), _mod_revision.NULL_REVISION)
1904
return list(self.revision_trees([revision_id]))[0]
1211
1906
def get_serializer_format(self):
1213
return self._real_repository.get_serializer_format()
1907
path = self.controldir._path_for_remote_call(self._client)
1909
response = self._call(b'VersionedFileRepository.get_serializer_format',
1911
except errors.UnknownSmartMethod:
1913
return self._real_repository.get_serializer_format()
1914
if response[0] != b'ok':
1915
raise errors.UnexpectedSmartServerResponse(response)
1215
1918
def get_commit_builder(self, branch, parents, config, timestamp=None,
1216
1919
timezone=None, committer=None, revprops=None,
1218
# FIXME: It ought to be possible to call this without immediately
1219
# triggering _ensure_real. For now it's the easiest thing to do.
1221
real_repo = self._real_repository
1222
builder = real_repo.get_commit_builder(branch, parents,
1223
config, timestamp=timestamp, timezone=timezone,
1224
committer=committer, revprops=revprops, revision_id=revision_id)
1920
revision_id=None, lossy=False):
1921
"""Obtain a CommitBuilder for this repository.
1923
:param branch: Branch to commit to.
1924
:param parents: Revision ids of the parents of the new revision.
1925
:param config: Configuration to use.
1926
:param timestamp: Optional timestamp recorded for commit.
1927
:param timezone: Optional timezone for timestamp.
1928
:param committer: Optional committer to set for commit.
1929
:param revprops: Optional dictionary of revision properties.
1930
:param revision_id: Optional revision id.
1931
:param lossy: Whether to discard data that can not be natively
1932
represented, when pushing to a foreign VCS
1934
if self._fallback_repositories and not self._format.supports_chks:
1935
raise errors.BzrError("Cannot commit directly to a stacked branch"
1936
" in pre-2a formats. See "
1937
"https://bugs.launchpad.net/bzr/+bug/375013 for details.")
1938
commit_builder_kls = vf_repository.VersionedFileCommitBuilder
1939
result = commit_builder_kls(self, parents, config,
1940
timestamp, timezone, committer, revprops, revision_id,
1942
self.start_write_group()
1227
1945
def add_fallback_repository(self, repository):
1228
1946
"""Add a repository to use for looking up data not held locally.
1266
1985
return self._real_repository.add_inventory(revid, inv, parents)
1268
1987
def add_inventory_by_delta(self, basis_revision_id, delta, new_revision_id,
1269
parents, basis_inv=None, propagate_caches=False):
1988
parents, basis_inv=None, propagate_caches=False):
1270
1989
self._ensure_real()
1271
1990
return self._real_repository.add_inventory_by_delta(basis_revision_id,
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)
1991
delta, new_revision_id, parents, basis_inv=basis_inv,
1992
propagate_caches=propagate_caches)
1994
def add_revision(self, revision_id, rev, inv=None):
1995
_mod_revision.check_not_reserved_id(revision_id)
1996
key = (revision_id,)
1997
# check inventory present
1998
if not self.inventories.get_parent_map([key]):
2000
raise errors.WeaveRevisionNotPresent(revision_id,
2003
# yes, this is not suitable for adding with ghosts.
2004
rev.inventory_sha1 = self.add_inventory(revision_id, inv,
2007
rev.inventory_sha1 = self.inventories.get_sha1s([key])[key]
2008
self._add_revision(rev)
2010
def _add_revision(self, rev):
2011
if self._real_repository is not None:
2012
return self._real_repository._add_revision(rev)
2013
lines = self._serializer.write_revision_to_lines(rev)
2014
key = (rev.revision_id,)
2015
parents = tuple((parent,) for parent in rev.parent_ids)
2016
self._write_group_tokens, missing_keys = self._get_sink().insert_stream(
2017
[('revisions', [ChunkedContentFactory(key, parents, None, lines, chunks_are_lines=True)])],
2018
self._format, self._write_group_tokens)
1281
2020
def get_inventory(self, revision_id):
2021
with self.lock_read():
2022
return list(self.iter_inventories([revision_id]))[0]
2024
def _iter_inventories_rpc(self, revision_ids, ordering):
2025
if ordering is None:
2026
ordering = 'unordered'
2027
path = self.controldir._path_for_remote_call(self._client)
2028
body = b"\n".join(revision_ids)
2029
response_tuple, response_handler = (
2030
self._call_with_body_bytes_expecting_body(
2031
b"VersionedFileRepository.get_inventories",
2032
(path, ordering.encode('ascii')), body))
2033
if response_tuple[0] != b"ok":
2034
raise errors.UnexpectedSmartServerResponse(response_tuple)
2035
deserializer = inventory_delta.InventoryDeltaDeserializer()
2036
byte_stream = response_handler.read_streamed_body()
2037
decoded = smart_repo._byte_stream_to_stream(byte_stream)
2039
# no results whatsoever
2041
src_format, stream = decoded
2042
if src_format.network_name() != self._format.network_name():
2043
raise AssertionError(
2044
"Mismatched RemoteRepository and stream src %r, %r" % (
2045
src_format.network_name(), self._format.network_name()))
2046
# ignore the src format, it's not really relevant
2047
prev_inv = Inventory(root_id=None,
2048
revision_id=_mod_revision.NULL_REVISION)
2049
# there should be just one substream, with inventory deltas
2051
substream_kind, substream = next(stream)
2052
except StopIteration:
2054
if substream_kind != "inventory-deltas":
2055
raise AssertionError(
2056
"Unexpected stream %r received" % substream_kind)
2057
for record in substream:
2058
(parent_id, new_id, versioned_root, tree_references, invdelta) = (
2059
deserializer.parse_text_bytes(record.get_bytes_as("lines")))
2060
if parent_id != prev_inv.revision_id:
2061
raise AssertionError("invalid base %r != %r" % (parent_id,
2062
prev_inv.revision_id))
2063
inv = prev_inv.create_by_apply_delta(invdelta, new_id)
2064
yield inv, inv.revision_id
2067
def _iter_inventories_vfs(self, revision_ids, ordering=None):
1282
2068
self._ensure_real()
1283
return self._real_repository.get_inventory(revision_id)
2069
return self._real_repository._iter_inventories(revision_ids, ordering)
1285
2071
def iter_inventories(self, revision_ids, ordering=None):
1287
return self._real_repository.iter_inventories(revision_ids, ordering)
2072
"""Get many inventories by revision_ids.
2074
This will buffer some or all of the texts used in constructing the
2075
inventories in memory, but will only parse a single inventory at a
2078
:param revision_ids: The expected revision ids of the inventories.
2079
:param ordering: optional ordering, e.g. 'topological'. If not
2080
specified, the order of revision_ids will be preserved (by
2081
buffering if necessary).
2082
:return: An iterator of inventories.
2084
if ((None in revision_ids) or
2085
(_mod_revision.NULL_REVISION in revision_ids)):
2086
raise ValueError('cannot get null revision inventory')
2087
for inv, revid in self._iter_inventories(revision_ids, ordering):
2089
raise errors.NoSuchRevision(self, revid)
2092
def _iter_inventories(self, revision_ids, ordering=None):
2093
if len(revision_ids) == 0:
2095
missing = set(revision_ids)
2096
if ordering is None:
2097
order_as_requested = True
2099
order = list(revision_ids)
2101
next_revid = order.pop()
2103
order_as_requested = False
2104
if ordering != 'unordered' and self._fallback_repositories:
2105
raise ValueError('unsupported ordering %r' % ordering)
2106
iter_inv_fns = [self._iter_inventories_rpc] + [
2107
fallback._iter_inventories for fallback in
2108
self._fallback_repositories]
2110
for iter_inv in iter_inv_fns:
2111
request = [revid for revid in revision_ids if revid in missing]
2112
for inv, revid in iter_inv(request, ordering):
2115
missing.remove(inv.revision_id)
2116
if ordering != 'unordered':
2120
if order_as_requested:
2121
# Yield as many results as we can while preserving order.
2122
while next_revid in invs:
2123
inv = invs.pop(next_revid)
2124
yield inv, inv.revision_id
2126
next_revid = order.pop()
2128
# We still want to fully consume the stream, just
2129
# in case it is not actually finished at this point
2132
except errors.UnknownSmartMethod:
2133
for inv, revid in self._iter_inventories_vfs(revision_ids, ordering):
2137
if order_as_requested:
2138
if next_revid is not None:
2139
yield None, next_revid
2142
yield invs.get(revid), revid
2145
yield None, missing.pop()
1290
2147
def get_revision(self, revision_id):
1292
return self._real_repository.get_revision(revision_id)
2148
with self.lock_read():
2149
return self.get_revisions([revision_id])[0]
1294
2151
def get_transaction(self):
1295
2152
self._ensure_real()
1296
2153
return self._real_repository.get_transaction()
1299
def clone(self, a_bzrdir, revision_id=None):
1301
return self._real_repository.clone(a_bzrdir, revision_id=revision_id)
2155
def clone(self, a_controldir, revision_id=None):
2156
with self.lock_read():
2157
dest_repo = self._create_sprouting_repo(
2158
a_controldir, shared=self.is_shared())
2159
self.copy_content_into(dest_repo, revision_id)
1303
2162
def make_working_trees(self):
1304
2163
"""See Repository.make_working_trees"""
1306
return self._real_repository.make_working_trees()
2164
path = self.controldir._path_for_remote_call(self._client)
2166
response = self._call(b'Repository.make_working_trees', path)
2167
except errors.UnknownSmartMethod:
2169
return self._real_repository.make_working_trees()
2170
if response[0] not in (b'yes', b'no'):
2171
raise SmartProtocolError(
2172
'unexpected response code %s' % (response,))
2173
return response[0] == b'yes'
1308
2175
def refresh_data(self):
1309
"""Re-read any data needed to to synchronise with disk.
2176
"""Re-read any data needed to synchronise with disk.
1311
2178
This method is intended to be called after another repository instance
1312
2179
(such as one used by a smart server) has inserted data into the
1313
repository. It may not be called during a write group, but may be
1314
called at any other time.
2180
repository. On all repositories this will work outside of write groups.
2181
Some repository formats (pack and newer for breezy native formats)
2182
support refresh_data inside write groups. If called inside a write
2183
group on a repository that does not support refreshing in a write group
2184
IsInWriteGroupError will be raised.
1316
if self.is_in_write_group():
1317
raise errors.InternalBzrError(
1318
"May not refresh_data while in a write group.")
1319
2186
if self._real_repository is not None:
1320
2187
self._real_repository.refresh_data()
2188
# Refresh the parents cache for this object
2189
self._unstacked_provider.disable_cache()
2190
self._unstacked_provider.enable_cache()
1322
2192
def revision_ids_to_search_result(self, result_set):
1323
2193
"""Convert a set of revision ids to a graph SearchResult."""
1324
2194
result_parents = set()
1325
for parents in self.get_graph().get_parent_map(
1326
result_set).itervalues():
2195
for parents in self.get_graph().get_parent_map(result_set).values():
1327
2196
result_parents.update(parents)
1328
2197
included_keys = result_set.intersection(result_parents)
1329
2198
start_keys = result_set.difference(included_keys)
1330
2199
exclude_keys = result_parents.difference(result_set)
1331
result = graph.SearchResult(start_keys, exclude_keys,
1332
len(result_set), result_set)
2200
result = vf_search.SearchResult(start_keys, exclude_keys,
2201
len(result_set), result_set)
1336
def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
2204
def search_missing_revision_ids(self, other,
2205
find_ghosts=True, revision_ids=None, if_present_ids=None,
1337
2207
"""Return the revision ids that other has that this does not.
1339
2209
These are returned in topological order.
1341
2211
revision_id: only return revision ids included by revision_id.
1343
return repository.InterRepository.get(
1344
other, self).search_missing_revision_ids(revision_id, find_ghosts)
2213
with self.lock_read():
2214
inter_repo = _mod_repository.InterRepository.get(other, self)
2215
return inter_repo.search_missing_revision_ids(
2216
find_ghosts=find_ghosts, revision_ids=revision_ids,
2217
if_present_ids=if_present_ids, limit=limit)
1346
def fetch(self, source, revision_id=None, pb=None, find_ghosts=False,
2219
def fetch(self, source, revision_id=None, find_ghosts=False,
2220
fetch_spec=None, lossy=False):
1348
2221
# No base implementation to use as RemoteRepository is not a subclass
1349
2222
# of Repository; so this is a copy of Repository.fetch().
1350
2223
if fetch_spec is not None and revision_id is not None:
1388
2261
return self._real_repository._get_versioned_file_checker(
1389
2262
revisions, revision_versions_cache)
2264
def _iter_files_bytes_rpc(self, desired_files, absent):
2265
path = self.controldir._path_for_remote_call(self._client)
2268
for (file_id, revid, identifier) in desired_files:
2269
lines.append(b''.join([
2273
identifiers.append(identifier)
2274
(response_tuple, response_handler) = (
2275
self._call_with_body_bytes_expecting_body(
2276
b"Repository.iter_files_bytes", (path, ), b"\n".join(lines)))
2277
if response_tuple != (b'ok', ):
2278
response_handler.cancel_read_body()
2279
raise errors.UnexpectedSmartServerResponse(response_tuple)
2280
byte_stream = response_handler.read_streamed_body()
2282
def decompress_stream(start, byte_stream, unused):
2283
decompressor = zlib.decompressobj()
2284
yield decompressor.decompress(start)
2285
while decompressor.unused_data == b"":
2287
data = next(byte_stream)
2288
except StopIteration:
2290
yield decompressor.decompress(data)
2291
yield decompressor.flush()
2292
unused.append(decompressor.unused_data)
2295
while b"\n" not in unused:
2297
unused += next(byte_stream)
2298
except StopIteration:
2300
header, rest = unused.split(b"\n", 1)
2301
args = header.split(b"\0")
2302
if args[0] == b"absent":
2303
absent[identifiers[int(args[3])]] = (args[1], args[2])
2306
elif args[0] == b"ok":
2309
raise errors.UnexpectedSmartServerResponse(args)
2311
yield (identifiers[idx],
2312
decompress_stream(rest, byte_stream, unused_chunks))
2313
unused = b"".join(unused_chunks)
1391
2315
def iter_files_bytes(self, desired_files):
1392
2316
"""See Repository.iter_file_bytes.
1395
return self._real_repository.iter_files_bytes(desired_files)
2320
for (identifier, bytes_iterator) in self._iter_files_bytes_rpc(
2321
desired_files, absent):
2322
yield identifier, bytes_iterator
2323
for fallback in self._fallback_repositories:
2326
desired_files = [(key[0], key[1], identifier)
2327
for identifier, key in absent.items()]
2328
for (identifier, bytes_iterator) in fallback.iter_files_bytes(desired_files):
2329
del absent[identifier]
2330
yield identifier, bytes_iterator
2332
# There may be more missing items, but raise an exception
2334
missing_identifier = next(iter(absent))
2335
missing_key = absent[missing_identifier]
2336
raise errors.RevisionNotPresent(revision_id=missing_key[1],
2337
file_id=missing_key[0])
2338
except errors.UnknownSmartMethod:
2340
for (identifier, bytes_iterator) in (
2341
self._real_repository.iter_files_bytes(desired_files)):
2342
yield identifier, bytes_iterator
2344
def get_cached_parent_map(self, revision_ids):
2345
"""See breezy.CachingParentsProvider.get_cached_parent_map"""
2346
return self._unstacked_provider.get_cached_parent_map(revision_ids)
1397
2348
def get_parent_map(self, revision_ids):
1398
"""See bzrlib.Graph.get_parent_map()."""
2349
"""See breezy.Graph.get_parent_map()."""
1399
2350
return self._make_parents_provider().get_parent_map(revision_ids)
1401
2352
def _get_parent_map_rpc(self, keys):
1529
2469
revision_graph[d[0]] = (NULL_REVISION,)
1530
2470
return revision_graph
1533
2472
def get_signature_text(self, revision_id):
1535
return self._real_repository.get_signature_text(revision_id)
2473
with self.lock_read():
2474
path = self.controldir._path_for_remote_call(self._client)
2476
response_tuple, response_handler = self._call_expecting_body(
2477
b'Repository.get_revision_signature_text', path, revision_id)
2478
except errors.UnknownSmartMethod:
2480
return self._real_repository.get_signature_text(revision_id)
2481
except errors.NoSuchRevision as err:
2482
for fallback in self._fallback_repositories:
2484
return fallback.get_signature_text(revision_id)
2485
except errors.NoSuchRevision:
2489
if response_tuple[0] != b'ok':
2490
raise errors.UnexpectedSmartServerResponse(response_tuple)
2491
return response_handler.read_body_bytes()
1538
2493
def _get_inventory_xml(self, revision_id):
1540
return self._real_repository._get_inventory_xml(revision_id)
2494
with self.lock_read():
2495
# This call is used by older working tree formats,
2496
# which stored a serialized basis inventory.
2498
return self._real_repository._get_inventory_xml(revision_id)
1542
2500
def reconcile(self, other=None, thorough=False):
1544
return self._real_repository.reconcile(other=other, thorough=thorough)
2501
from ..reconcile import ReconcileResult
2502
with self.lock_write():
2503
path = self.controldir._path_for_remote_call(self._client)
2505
response, handler = self._call_expecting_body(
2506
b'Repository.reconcile', path, self._lock_token)
2507
except (errors.UnknownSmartMethod, errors.TokenLockingNotSupported):
2509
return self._real_repository.reconcile(other=other, thorough=thorough)
2510
if response != (b'ok', ):
2511
raise errors.UnexpectedSmartServerResponse(response)
2512
body = handler.read_body_bytes()
2513
result = ReconcileResult()
2514
result.garbage_inventories = None
2515
result.inconsistent_parents = None
2516
result.aborted = None
2517
for line in body.split(b'\n'):
2520
key, val_text = line.split(b':')
2521
if key == b"garbage_inventories":
2522
result.garbage_inventories = int(val_text)
2523
elif key == b"inconsistent_parents":
2524
result.inconsistent_parents = int(val_text)
2526
mutter("unknown reconcile key %r" % key)
1546
2529
def all_revision_ids(self):
1548
return self._real_repository.all_revision_ids()
1551
def get_deltas_for_revisions(self, revisions, specific_fileids=None):
1553
return self._real_repository.get_deltas_for_revisions(revisions,
1554
specific_fileids=specific_fileids)
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)
2530
path = self.controldir._path_for_remote_call(self._client)
2532
response_tuple, response_handler = self._call_expecting_body(
2533
b"Repository.all_revision_ids", path)
2534
except errors.UnknownSmartMethod:
2536
return self._real_repository.all_revision_ids()
2537
if response_tuple != (b"ok", ):
2538
raise errors.UnexpectedSmartServerResponse(response_tuple)
2539
revids = set(response_handler.read_body_bytes().splitlines())
2540
for fallback in self._fallback_repositories:
2541
revids.update(set(fallback.all_revision_ids()))
2544
def _filtered_revision_trees(self, revision_ids, file_ids):
2545
"""Return Tree for a revision on this branch with only some files.
2547
:param revision_ids: a sequence of revision-ids;
2548
a revision-id may not be None or b'null:'
2549
:param file_ids: if not None, the result is filtered
2550
so that only those file-ids, their parents and their
2551
children are included.
2553
inventories = self.iter_inventories(revision_ids)
2554
for inv in inventories:
2555
# Should we introduce a FilteredRevisionTree class rather
2556
# than pre-filter the inventory here?
2557
filtered_inv = inv.filter(file_ids)
2558
yield InventoryRevisionTree(self, filtered_inv, filtered_inv.revision_id)
2560
def get_revision_delta(self, revision_id):
2561
with self.lock_read():
2562
r = self.get_revision(revision_id)
2563
return list(self.get_revision_deltas([r]))[0]
1563
2565
def revision_trees(self, revision_ids):
1565
return self._real_repository.revision_trees(revision_ids)
2566
with self.lock_read():
2567
inventories = self.iter_inventories(revision_ids)
2568
for inv in inventories:
2569
yield RemoteInventoryTree(self, inv, inv.revision_id)
1568
2571
def get_revision_reconcile(self, revision_id):
1570
return self._real_repository.get_revision_reconcile(revision_id)
2572
with self.lock_read():
2574
return self._real_repository.get_revision_reconcile(revision_id)
1573
2576
def check(self, revision_ids=None, callback_refs=None, check_repo=True):
1575
return self._real_repository.check(revision_ids=revision_ids,
1576
callback_refs=callback_refs, check_repo=check_repo)
2577
with self.lock_read():
2579
return self._real_repository.check(revision_ids=revision_ids,
2580
callback_refs=callback_refs, check_repo=check_repo)
1578
2582
def copy_content_into(self, destination, revision_id=None):
1580
return self._real_repository.copy_content_into(
1581
destination, revision_id=revision_id)
2583
"""Make a complete copy of the content in self into destination.
2585
This is a destructive operation! Do not use it on existing
2588
interrepo = _mod_repository.InterRepository.get(self, destination)
2589
return interrepo.copy_content(revision_id)
1583
2591
def _copy_repository_tarball(self, to_bzrdir, revision_id=None):
1584
2592
# get a tarball of the remote repository, and copy from that into the
1586
from bzrlib import osutils
1588
2595
# TODO: Maybe a progress bar while streaming the tarball?
1589
note("Copying repository content as tarball...")
2596
note(gettext("Copying repository content as tarball..."))
1590
2597
tar_file = self._get_tarball('bz2')
1591
2598
if tar_file is None:
1593
2600
destination = to_bzrdir.create_repository()
1595
2602
tar = tarfile.open('repository', fileobj=tar_file,
1597
2604
tmpdir = osutils.mkdtemp()
1599
_extract_tar(tar, tmpdir)
1600
tmp_bzrdir = BzrDir.open(tmpdir)
2606
tar.extractall(tmpdir)
2607
tmp_bzrdir = _mod_bzrdir.BzrDir.open(tmpdir)
1601
2608
tmp_repo = tmp_bzrdir.open_repository()
1602
2609
tmp_repo.copy_content_into(destination, revision_id)
1680
2698
self._ensure_real()
1681
2699
return self._real_repository.texts
1684
def get_revisions(self, revision_ids):
1686
return self._real_repository.get_revisions(revision_ids)
2701
def _iter_revisions_rpc(self, revision_ids):
2702
body = b"\n".join(revision_ids)
2703
path = self.controldir._path_for_remote_call(self._client)
2704
response_tuple, response_handler = (
2705
self._call_with_body_bytes_expecting_body(
2706
b"Repository.iter_revisions", (path, ), body))
2707
if response_tuple[0] != b"ok":
2708
raise errors.UnexpectedSmartServerResponse(response_tuple)
2709
serializer_format = response_tuple[1].decode('ascii')
2710
serializer = serializer_format_registry.get(serializer_format)
2711
byte_stream = response_handler.read_streamed_body()
2712
decompressor = zlib.decompressobj()
2714
for bytes in byte_stream:
2715
chunks.append(decompressor.decompress(bytes))
2716
if decompressor.unused_data != b"":
2717
chunks.append(decompressor.flush())
2718
yield serializer.read_revision_from_string(b"".join(chunks))
2719
unused = decompressor.unused_data
2720
decompressor = zlib.decompressobj()
2721
chunks = [decompressor.decompress(unused)]
2722
chunks.append(decompressor.flush())
2723
text = b"".join(chunks)
2725
yield serializer.read_revision_from_string(b"".join(chunks))
2727
def iter_revisions(self, revision_ids):
2728
for rev_id in revision_ids:
2729
if not rev_id or not isinstance(rev_id, bytes):
2730
raise errors.InvalidRevisionId(
2731
revision_id=rev_id, branch=self)
2732
with self.lock_read():
2734
missing = set(revision_ids)
2735
for rev in self._iter_revisions_rpc(revision_ids):
2736
missing.remove(rev.revision_id)
2737
yield (rev.revision_id, rev)
2738
for fallback in self._fallback_repositories:
2741
for (revid, rev) in fallback.iter_revisions(missing):
2744
missing.remove(revid)
2745
for revid in missing:
2747
except errors.UnknownSmartMethod:
2749
for entry in self._real_repository.iter_revisions(revision_ids):
1688
2752
def supports_rich_root(self):
1689
2753
return self._format.rich_root_data
1691
def iter_reverse_revision_history(self, revision_id):
1693
return self._real_repository.iter_reverse_revision_history(revision_id)
1696
2756
def _serializer(self):
1697
2757
return self._format._serializer
1699
2759
def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
1701
return self._real_repository.store_revision_signature(
1702
gpg_strategy, plaintext, revision_id)
2760
with self.lock_write():
2761
signature = gpg_strategy.sign(plaintext, gpg.MODE_CLEAR)
2762
self.add_signature_text(revision_id, signature)
1704
2764
def add_signature_text(self, revision_id, signature):
1706
return self._real_repository.add_signature_text(revision_id, signature)
2765
if self._real_repository:
2766
# If there is a real repository the write group will
2767
# be in the real repository as well, so use that:
2769
return self._real_repository.add_signature_text(
2770
revision_id, signature)
2771
path = self.controldir._path_for_remote_call(self._client)
2772
response, handler = self._call_with_body_bytes_expecting_body(
2773
b'Repository.add_signature_text', (path, self._lock_token,
2775
tuple([token.encode('utf-8')
2776
for token in self._write_group_tokens]),
2778
handler.cancel_read_body()
2780
if response[0] != b'ok':
2781
raise errors.UnexpectedSmartServerResponse(response)
2782
self._write_group_tokens = [token.decode(
2783
'utf-8') for token in response[1:]]
1708
2785
def has_signature_for_revision_id(self, revision_id):
1710
return self._real_repository.has_signature_for_revision_id(revision_id)
2786
path = self.controldir._path_for_remote_call(self._client)
2788
response = self._call(b'Repository.has_signature_for_revision_id',
2790
except errors.UnknownSmartMethod:
2792
return self._real_repository.has_signature_for_revision_id(
2794
if response[0] not in (b'yes', b'no'):
2795
raise SmartProtocolError(
2796
'unexpected response code %s' % (response,))
2797
if response[0] == b'yes':
2799
for fallback in self._fallback_repositories:
2800
if fallback.has_signature_for_revision_id(revision_id):
2804
def verify_revision_signature(self, revision_id, gpg_strategy):
2805
with self.lock_read():
2806
if not self.has_signature_for_revision_id(revision_id):
2807
return gpg.SIGNATURE_NOT_SIGNED, None
2808
signature = self.get_signature_text(revision_id)
2810
testament = _mod_testament.Testament.from_revision(
2813
(status, key, signed_plaintext) = gpg_strategy.verify(signature)
2814
if testament.as_short_text() != signed_plaintext:
2815
return gpg.SIGNATURE_NOT_VALID, None
2816
return (status, key)
1712
2818
def item_keys_introduced_by(self, revision_ids, _files_pb=None):
1713
2819
self._ensure_real()
1714
2820
return self._real_repository.item_keys_introduced_by(revision_ids,
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()
2821
_files_pb=_files_pb)
1722
2823
def _find_inconsistent_revision_parents(self, revisions_iterator=None):
1723
2824
self._ensure_real()
1742
2842
:param recipe: A search recipe (start, stop, count).
1743
2843
:return: Serialised bytes.
1745
start_keys = ' '.join(recipe[1])
1746
stop_keys = ' '.join(recipe[2])
1747
count = str(recipe[3])
1748
return '\n'.join((start_keys, stop_keys, count))
2845
start_keys = b' '.join(recipe[1])
2846
stop_keys = b' '.join(recipe[2])
2847
count = str(recipe[3]).encode('ascii')
2848
return b'\n'.join((start_keys, stop_keys, count))
1750
2850
def _serialise_search_result(self, search_result):
1751
if isinstance(search_result, graph.PendingAncestryResult):
1752
parts = ['ancestry-of']
1753
parts.extend(search_result.heads)
1755
recipe = search_result.get_recipe()
1756
parts = [recipe[0], self._serialise_search_recipe(recipe)]
1757
return '\n'.join(parts)
2851
parts = search_result.get_network_struct()
2852
return b'\n'.join(parts)
1759
2854
def autopack(self):
1760
path = self.bzrdir._path_for_remote_call(self._client)
2855
path = self.controldir._path_for_remote_call(self._client)
1762
response = self._call('PackRepository.autopack', path)
2857
response = self._call(b'PackRepository.autopack', path)
1763
2858
except errors.UnknownSmartMethod:
1764
2859
self._ensure_real()
1765
2860
self._real_repository._pack_collection.autopack()
1767
2862
self.refresh_data()
1768
if response[0] != 'ok':
1769
raise errors.UnexpectedSmartServerResponse(response)
1772
class RemoteStreamSink(repository.StreamSink):
2863
if response[0] != b'ok':
2864
raise errors.UnexpectedSmartServerResponse(response)
2866
def _revision_archive(self, revision_id, format, name, root, subdir,
2868
path = self.controldir._path_for_remote_call(self._client)
2869
format = format or ''
2871
subdir = subdir or ''
2872
force_mtime = int(force_mtime) if force_mtime is not None else None
2874
response, protocol = self._call_expecting_body(
2875
b'Repository.revision_archive', path,
2877
format.encode('ascii'),
2878
os.path.basename(name).encode('utf-8'),
2879
root.encode('utf-8'),
2880
subdir.encode('utf-8'),
2882
except errors.UnknownSmartMethod:
2884
if response[0] == b'ok':
2885
return iter([protocol.read_body_bytes()])
2886
raise errors.UnexpectedSmartServerResponse(response)
2888
def _annotate_file_revision(self, revid, tree_path, file_id, default_revision):
2889
path = self.controldir._path_for_remote_call(self._client)
2890
tree_path = tree_path.encode('utf-8')
2891
file_id = file_id or b''
2892
default_revision = default_revision or b''
2894
response, handler = self._call_expecting_body(
2895
b'Repository.annotate_file_revision', path,
2896
revid, tree_path, file_id, default_revision)
2897
except errors.UnknownSmartMethod:
2899
if response[0] != b'ok':
2900
raise errors.UnexpectedSmartServerResponse(response)
2901
return map(tuple, bencode.bdecode(handler.read_body_bytes()))
2904
class RemoteStreamSink(vf_repository.StreamSink):
1774
2906
def _insert_real(self, stream, src_format, resume_tokens):
1775
2907
self.target_repo._ensure_real()
2059
3251
def network_name(self):
2060
3252
return self._network_name
2062
def open(self, a_bzrdir, name=None, ignore_fallbacks=False):
2063
return a_bzrdir.open_branch(name=name,
2064
ignore_fallbacks=ignore_fallbacks)
3254
def open(self, a_controldir, name=None, ignore_fallbacks=False):
3255
return a_controldir.open_branch(name=name,
3256
ignore_fallbacks=ignore_fallbacks)
2066
def _vfs_initialize(self, a_bzrdir, name):
3258
def _vfs_initialize(self, a_controldir, name, append_revisions_only,
2067
3260
# Initialisation when using a local bzrdir object, or a non-vfs init
2068
3261
# method is not available on the server.
2069
3262
# self._custom_format is always set - the start of initialize ensures
2071
if isinstance(a_bzrdir, RemoteBzrDir):
2072
a_bzrdir._ensure_real()
2073
result = self._custom_format.initialize(a_bzrdir._real_bzrdir,
3264
if isinstance(a_controldir, RemoteBzrDir):
3265
a_controldir._ensure_real()
3266
result = self._custom_format.initialize(a_controldir._real_bzrdir,
3267
name=name, append_revisions_only=append_revisions_only,
3268
repository=repository)
2076
3270
# We assume the bzrdir is parameterised; it may not be.
2077
result = self._custom_format.initialize(a_bzrdir, name)
2078
if (isinstance(a_bzrdir, RemoteBzrDir) and
2079
not isinstance(result, RemoteBranch)):
2080
result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result,
3271
result = self._custom_format.initialize(a_controldir, name=name,
3272
append_revisions_only=append_revisions_only,
3273
repository=repository)
3274
if (isinstance(a_controldir, RemoteBzrDir)
3275
and not isinstance(result, RemoteBranch)):
3276
result = RemoteBranch(a_controldir, a_controldir.find_repository(), result,
2084
def initialize(self, a_bzrdir, name=None):
3280
def initialize(self, a_controldir, name=None, repository=None,
3281
append_revisions_only=None):
3283
name = a_controldir._get_selected_branch()
2085
3284
# 1) get the network name to use.
2086
3285
if self._custom_format:
2087
3286
network_name = self._custom_format.network_name()
2089
# Select the current bzrlib default and ask for that.
2090
reference_bzrdir_format = bzrdir.format_registry.get('default')()
3288
# Select the current breezy default and ask for that.
3289
reference_bzrdir_format = controldir.format_registry.get(
2091
3291
reference_format = reference_bzrdir_format.get_branch_format()
2092
3292
self._custom_format = reference_format
2093
3293
network_name = reference_format.network_name()
2094
3294
# Being asked to create on a non RemoteBzrDir:
2095
if not isinstance(a_bzrdir, RemoteBzrDir):
2096
return self._vfs_initialize(a_bzrdir, name=name)
2097
medium = a_bzrdir._client._medium
3295
if not isinstance(a_controldir, RemoteBzrDir):
3296
return self._vfs_initialize(a_controldir, name=name,
3297
append_revisions_only=append_revisions_only,
3298
repository=repository)
3299
medium = a_controldir._client._medium
2098
3300
if medium._is_remote_before((1, 13)):
2099
return self._vfs_initialize(a_bzrdir, name=name)
3301
return self._vfs_initialize(a_controldir, name=name,
3302
append_revisions_only=append_revisions_only,
3303
repository=repository)
2100
3304
# Creating on a remote bzr dir.
2101
3305
# 2) try direct creation via RPC
2102
path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
2103
if name is not None:
3306
path = a_controldir._path_for_remote_call(a_controldir._client)
2104
3308
# XXX JRV20100304: Support creating colocated branches
2105
3309
raise errors.NoColocatedBranchSupport(self)
2106
verb = 'BzrDir.create_branch'
3310
verb = b'BzrDir.create_branch'
2108
response = a_bzrdir._call(verb, path, network_name)
3312
response = a_controldir._call(verb, path, network_name)
2109
3313
except errors.UnknownSmartMethod:
2110
3314
# Fallback - use vfs methods
2111
3315
medium._remember_remote_is_before((1, 13))
2112
return self._vfs_initialize(a_bzrdir, name=name)
2113
if response[0] != 'ok':
3316
return self._vfs_initialize(a_controldir, name=name,
3317
append_revisions_only=append_revisions_only,
3318
repository=repository)
3319
if response[0] != b'ok':
2114
3320
raise errors.UnexpectedSmartServerResponse(response)
2115
3321
# Turn the response into a RemoteRepository object.
2116
3322
format = RemoteBranchFormat(network_name=response[1])
2117
3323
repo_format = response_tuple_to_repo_format(response[3:])
2118
if response[2] == '':
2119
repo_bzrdir = a_bzrdir
3324
repo_path = response[2].decode('utf-8')
3325
if repository is not None:
3326
remote_repo_url = urlutils.join(a_controldir.user_url, repo_path)
3327
url_diff = urlutils.relative_url(repository.user_url,
3330
raise AssertionError(
3331
'repository.user_url %r does not match URL from server '
3332
'response (%r + %r)'
3333
% (repository.user_url, a_controldir.user_url, repo_path))
3334
remote_repo = repository
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)
3337
repo_bzrdir = a_controldir
3339
repo_bzrdir = RemoteBzrDir(
3340
a_controldir.root_transport.clone(
3341
repo_path), a_controldir._format,
3342
a_controldir._client)
3343
remote_repo = RemoteRepository(repo_bzrdir, repo_format)
3344
remote_branch = RemoteBranch(a_controldir, remote_repo,
3345
format=format, setup_stacking=False, name=name)
3346
if append_revisions_only:
3347
remote_branch.set_append_revisions_only(append_revisions_only)
2127
3348
# XXX: We know this is a new branch, so it must have revno 0, revid
2128
3349
# NULL_REVISION. Creating the branch locked would make this be unable
2129
3350
# to be wrong; here its simply very unlikely to be wrong. RBC 20090225
2148
3369
self._ensure_real()
2149
3370
return self._custom_format.supports_set_append_revisions_only()
3373
def supports_reference_locations(self):
3375
return self._custom_format.supports_reference_locations
3377
def stores_revno(self):
3380
def _use_default_local_heads_to_fetch(self):
3381
# If the branch format is a metadir format *and* its heads_to_fetch
3382
# implementation is not overridden vs the base class, we can use the
3383
# base class logic rather than use the heads_to_fetch RPC. This is
3384
# usually cheaper in terms of net round trips, as the last-revision and
3385
# tags info fetched is cached and would be fetched anyway.
3387
if isinstance(self._custom_format, bzrbranch.BranchFormatMetadir):
3388
branch_class = self._custom_format._branch_class()
3389
heads_to_fetch_impl = branch_class.heads_to_fetch
3390
if heads_to_fetch_impl is branch.Branch.heads_to_fetch:
3395
class RemoteBranchStore(_mod_config.IniFileStore):
3396
"""Branch store which attempts to use HPSS calls to retrieve branch store.
3398
Note that this is specific to bzr-based formats.
3401
def __init__(self, branch):
3402
super(RemoteBranchStore, self).__init__()
3403
self.branch = branch
3405
self._real_store = None
3407
def external_url(self):
3408
return urlutils.join(self.branch.user_url, 'branch.conf')
3410
def _load_content(self):
3411
path = self.branch._remote_path()
3413
response, handler = self.branch._call_expecting_body(
3414
b'Branch.get_config_file', path)
3415
except errors.UnknownSmartMethod:
3417
return self._real_store._load_content()
3418
if len(response) and response[0] != b'ok':
3419
raise errors.UnexpectedSmartServerResponse(response)
3420
return handler.read_body_bytes()
3422
def _save_content(self, content):
3423
path = self.branch._remote_path()
3425
response, handler = self.branch._call_with_body_bytes_expecting_body(
3426
b'Branch.put_config_file', (path,
3427
self.branch._lock_token, self.branch._repo_lock_token),
3429
except errors.UnknownSmartMethod:
3431
return self._real_store._save_content(content)
3432
handler.cancel_read_body()
3433
if response != (b'ok', ):
3434
raise errors.UnexpectedSmartServerResponse(response)
3436
def _ensure_real(self):
3437
self.branch._ensure_real()
3438
if self._real_store is None:
3439
self._real_store = _mod_config.BranchStore(self.branch)
2152
3442
class RemoteBranch(branch.Branch, _RpcHelper, lock._RelockDebugMixin):
2153
3443
"""Branch stored on a server accessed by HPSS RPC.
2644
3992
self._ensure_real()
2645
3993
return self._real_branch._set_parent_location(url)
2648
3995
def pull(self, source, overwrite=False, stop_revision=None,
2650
self._clear_cached_state_of_remote_branch_only()
2652
return self._real_branch.pull(
2653
source, overwrite=overwrite, stop_revision=stop_revision,
2654
_override_hook_target=self, **kwargs)
2657
def push(self, target, overwrite=False, stop_revision=None):
2659
return self._real_branch.push(
2660
target, overwrite=overwrite, stop_revision=stop_revision,
2661
_override_hook_source_branch=self)
3997
with self.lock_write():
3998
self._clear_cached_state_of_remote_branch_only()
4000
return self._real_branch.pull(
4001
source, overwrite=overwrite, stop_revision=stop_revision,
4002
_override_hook_target=self, **kwargs)
4004
def push(self, target, overwrite=False, stop_revision=None, lossy=False, tag_selector=None):
4005
with self.lock_read():
4007
return self._real_branch.push(
4008
target, overwrite=overwrite, stop_revision=stop_revision, lossy=lossy,
4009
_override_hook_source_branch=self, tag_selector=tag_selector)
4011
def peek_lock_mode(self):
4012
return self._lock_mode
2663
4014
def is_locked(self):
2664
4015
return self._lock_count >= 1
4017
def revision_id_to_dotted_revno(self, revision_id):
4018
"""Given a revision id, return its dotted revno.
4020
:return: a tuple like (1,) or (400,1,3).
4022
with self.lock_read():
4024
response = self._call(b'Branch.revision_id_to_revno',
4025
self._remote_path(), revision_id)
4026
except errors.UnknownSmartMethod:
4028
return self._real_branch.revision_id_to_dotted_revno(revision_id)
4029
except errors.UnknownErrorFromSmartServer as e:
4030
# Deal with older versions of bzr/brz that didn't explicitly
4031
# wrap GhostRevisionsHaveNoRevno.
4032
if e.error_tuple[1] == b'GhostRevisionsHaveNoRevno':
4033
(revid, ghost_revid) = re.findall(b"{([^}]+)}", e.error_tuple[2])
4034
raise errors.GhostRevisionsHaveNoRevno(
4037
if response[0] == b'ok':
4038
return tuple([int(x) for x in response[1:]])
4040
raise errors.UnexpectedSmartServerResponse(response)
2667
4042
def revision_id_to_revno(self, revision_id):
2669
return self._real_branch.revision_id_to_revno(revision_id)
4043
"""Given a revision id on the branch mainline, return its revno.
4047
with self.lock_read():
4049
response = self._call(b'Branch.revision_id_to_revno',
4050
self._remote_path(), revision_id)
4051
except errors.UnknownSmartMethod:
4053
return self._real_branch.revision_id_to_revno(revision_id)
4054
if response[0] == b'ok':
4055
if len(response) == 2:
4056
return int(response[1])
4057
raise NoSuchRevision(self, revision_id)
4059
raise errors.UnexpectedSmartServerResponse(response)
2672
4061
def set_last_revision_info(self, revno, revision_id):
2673
# XXX: These should be returned by the set_last_revision_info verb
2674
old_revno, old_revid = self.last_revision_info()
2675
self._run_pre_change_branch_tip_hooks(revno, revision_id)
2676
revision_id = ensure_null(revision_id)
2678
response = self._call('Branch.set_last_revision_info',
2679
self._remote_path(), self._lock_token, self._repo_lock_token,
2680
str(revno), revision_id)
2681
except errors.UnknownSmartMethod:
2683
self._clear_cached_state_of_remote_branch_only()
2684
self._real_branch.set_last_revision_info(revno, revision_id)
2685
self._last_revision_info_cache = revno, revision_id
2687
if response == ('ok',):
2688
self._clear_cached_state()
2689
self._last_revision_info_cache = revno, revision_id
2690
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2691
# Update the _real_branch's cache too.
2692
if self._real_branch is not None:
2693
cache = self._last_revision_info_cache
2694
self._real_branch._last_revision_info_cache = cache
2696
raise errors.UnexpectedSmartServerResponse(response)
4062
with self.lock_write():
4063
# XXX: These should be returned by the set_last_revision_info verb
4064
old_revno, old_revid = self.last_revision_info()
4065
self._run_pre_change_branch_tip_hooks(revno, revision_id)
4066
if not revision_id or not isinstance(revision_id, bytes):
4067
raise errors.InvalidRevisionId(
4068
revision_id=revision_id, branch=self)
4070
response = self._call(b'Branch.set_last_revision_info',
4071
self._remote_path(), self._lock_token, self._repo_lock_token,
4072
str(revno).encode('ascii'), revision_id)
4073
except errors.UnknownSmartMethod:
4075
self._clear_cached_state_of_remote_branch_only()
4076
self._real_branch.set_last_revision_info(revno, revision_id)
4077
self._last_revision_info_cache = revno, revision_id
4079
if response == (b'ok',):
4080
self._clear_cached_state()
4081
self._last_revision_info_cache = revno, revision_id
4082
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
4083
# Update the _real_branch's cache too.
4084
if self._real_branch is not None:
4085
cache = self._last_revision_info_cache
4086
self._real_branch._last_revision_info_cache = cache
4088
raise errors.UnexpectedSmartServerResponse(response)
2699
4090
def generate_revision_history(self, revision_id, last_rev=None,
2700
4091
other_branch=None):
2701
medium = self._client._medium
2702
if not medium._is_remote_before((1, 6)):
2703
# Use a smart method for 1.6 and above servers
2705
self._set_last_revision_descendant(revision_id, other_branch,
2706
allow_diverged=True, allow_overwrite_descendant=True)
2708
except errors.UnknownSmartMethod:
2709
medium._remember_remote_is_before((1, 6))
2710
self._clear_cached_state_of_remote_branch_only()
2711
self.set_revision_history(self._lefthand_history(revision_id,
2712
last_rev=last_rev,other_branch=other_branch))
4092
with self.lock_write():
4093
medium = self._client._medium
4094
if not medium._is_remote_before((1, 6)):
4095
# Use a smart method for 1.6 and above servers
4097
self._set_last_revision_descendant(revision_id, other_branch,
4098
allow_diverged=True, allow_overwrite_descendant=True)
4100
except errors.UnknownSmartMethod:
4101
medium._remember_remote_is_before((1, 6))
4102
self._clear_cached_state_of_remote_branch_only()
4103
graph = self.repository.get_graph()
4104
(last_revno, last_revid) = self.last_revision_info()
4105
known_revision_ids = [
4106
(last_revid, last_revno),
4107
(_mod_revision.NULL_REVISION, 0),
4109
if last_rev is not None:
4110
if not graph.is_ancestor(last_rev, revision_id):
4111
# our previous tip is not merged into stop_revision
4112
raise errors.DivergedBranches(self, other_branch)
4113
revno = graph.find_distance_to_null(
4114
revision_id, known_revision_ids)
4115
self.set_last_revision_info(revno, revision_id)
2714
4117
def set_push_location(self, location):
2716
return self._real_branch.set_push_location(location)
4118
self._set_config_location('push_location', location)
4120
def heads_to_fetch(self):
4121
if self._format._use_default_local_heads_to_fetch():
4122
# We recognise this format, and its heads-to-fetch implementation
4123
# is the default one (tip + tags). In this case it's cheaper to
4124
# just use the default implementation rather than a special RPC as
4125
# the tip and tags data is cached.
4126
return branch.Branch.heads_to_fetch(self)
4127
medium = self._client._medium
4128
if medium._is_remote_before((2, 4)):
4129
return self._vfs_heads_to_fetch()
4131
return self._rpc_heads_to_fetch()
4132
except errors.UnknownSmartMethod:
4133
medium._remember_remote_is_before((2, 4))
4134
return self._vfs_heads_to_fetch()
4136
def _rpc_heads_to_fetch(self):
4137
response = self._call(b'Branch.heads_to_fetch', self._remote_path())
4138
if len(response) != 2:
4139
raise errors.UnexpectedSmartServerResponse(response)
4140
must_fetch, if_present_fetch = response
4141
return set(must_fetch), set(if_present_fetch)
4143
def _vfs_heads_to_fetch(self):
4145
return self._real_branch.heads_to_fetch()
4147
def reconcile(self, thorough=True):
4148
"""Make sure the data stored in this branch is consistent."""
4149
from .reconcile import BranchReconciler
4150
with self.lock_write():
4151
reconciler = BranchReconciler(self, thorough=thorough)
4152
return reconciler.reconcile()
4154
def get_reference_info(self, file_id):
4155
"""Get the tree_path and branch_location for a tree reference."""
4156
if not self._format.supports_reference_locations:
4157
raise errors.UnsupportedOperation(self.get_reference_info, self)
4158
return self._get_all_reference_info().get(file_id, (None, None))
4160
def set_reference_info(self, file_id, branch_location, tree_path=None):
4161
"""Set the branch location to use for a tree reference."""
4162
if not self._format.supports_reference_locations:
4163
raise errors.UnsupportedOperation(self.set_reference_info, self)
4165
self._real_branch.set_reference_info(
4166
file_id, branch_location, tree_path)
4168
def _set_all_reference_info(self, reference_info):
4169
if not self._format.supports_reference_locations:
4170
raise errors.UnsupportedOperation(self.set_reference_info, self)
4172
self._real_branch._set_all_reference_info(reference_info)
4174
def _get_all_reference_info(self):
4175
if not self._format.supports_reference_locations:
4178
response, handler = self._call_expecting_body(
4179
b'Branch.get_all_reference_info', self._remote_path())
4180
except errors.UnknownSmartMethod:
4182
return self._real_branch._get_all_reference_info()
4183
if len(response) and response[0] != b'ok':
4184
raise errors.UnexpectedSmartServerResponse(response)
4186
for (f, u, p) in bencode.bdecode(handler.read_body_bytes()):
4187
ret[f] = (u.decode('utf-8'), p.decode('utf-8') if p else None)
4190
def reference_parent(self, file_id, path, possible_transports=None):
4191
"""Return the parent branch for a tree-reference.
4193
:param path: The path of the nested tree in the tree
4194
:return: A branch associated with the nested tree
4196
branch_location = self.get_reference_info(file_id)[0]
4197
if branch_location is None:
4199
return branch.Branch.open_from_transport(
4200
self.controldir.root_transport.clone(path),
4201
possible_transports=possible_transports)
4202
except errors.NotBranchError:
4204
return branch.Branch.open(
4206
urlutils.strip_segment_parameters(self.user_url), branch_location),
4207
possible_transports=possible_transports)
2719
4210
class RemoteConfig(object):
2774
4275
medium = self._branch._client._medium
2775
4276
if medium._is_remote_before((1, 14)):
2776
4277
return self._vfs_set_option(value, name, section)
2778
path = self._branch._remote_path()
2779
response = self._branch._client.call('Branch.set_config_option',
4278
if isinstance(value, dict):
4279
if medium._is_remote_before((2, 2)):
4280
return self._vfs_set_option(value, name, section)
4281
return self._set_config_option_dict(value, name, section)
4283
return self._set_config_option(value, name, section)
4285
def _set_config_option(self, value, name, section):
4286
if isinstance(value, (bool, int)):
4288
elif isinstance(value, str):
4291
raise TypeError(value)
4293
path = self._branch._remote_path()
4294
response = self._branch._client.call(b'Branch.set_config_option',
4295
path, self._branch._lock_token, self._branch._repo_lock_token,
4296
value.encode('utf-8'), name.encode('utf-8'),
4297
(section or '').encode('utf-8'))
4298
except errors.UnknownSmartMethod:
4299
medium = self._branch._client._medium
4300
medium._remember_remote_is_before((1, 14))
4301
return self._vfs_set_option(value, name, section)
4303
raise errors.UnexpectedSmartServerResponse(response)
4305
def _serialize_option_dict(self, option_dict):
4307
for key, value in option_dict.items():
4308
if isinstance(key, str):
4309
key = key.encode('utf8')
4310
if isinstance(value, str):
4311
value = value.encode('utf8')
4312
utf8_dict[key] = value
4313
return bencode.bencode(utf8_dict)
4315
def _set_config_option_dict(self, value, name, section):
4317
path = self._branch._remote_path()
4318
serialised_dict = self._serialize_option_dict(value)
4319
response = self._branch._client.call(
4320
b'Branch.set_config_option_dict',
2780
4321
path, self._branch._lock_token, self._branch._repo_lock_token,
2781
value.encode('utf8'), name, section or '')
4322
serialised_dict, name.encode('utf-8'), (section or '').encode('utf-8'))
2782
4323
except errors.UnknownSmartMethod:
2783
medium._remember_remote_is_before((1, 14))
4324
medium = self._branch._client._medium
4325
medium._remember_remote_is_before((2, 2))
2784
4326
return self._vfs_set_option(value, name, section)
2785
4327
if response != ():
2786
4328
raise errors.UnexpectedSmartServerResponse(response)
2856
4392
def find(name):
2858
4394
return context[name]
2859
except KeyError, key_err:
2860
mutter('Missing key %r in context %r', key_err.args[0], context)
4396
mutter('Missing key \'%s\' in context %r', name, context)
2862
4399
def get_path():
2863
4400
"""Get the path from the context if present, otherwise use first error
2867
4404
return context['path']
2868
except KeyError, key_err:
2870
return err.error_args[0]
2871
except IndexError, idx_err:
2873
'Missing key %r in context %r', key_err.args[0], context)
4407
return err.error_args[0].decode('utf-8')
4409
mutter('Missing key \'path\' in context %r', context)
2876
if err.error_verb == 'IncompatibleRepositories':
2877
raise errors.IncompatibleRepositories(err.error_args[0],
2878
err.error_args[1], err.error_args[2])
2879
elif err.error_verb == 'NoSuchRevision':
2880
raise NoSuchRevision(find('branch'), err.error_args[0])
2881
elif err.error_verb == 'nosuchrevision':
2882
raise NoSuchRevision(find('repository'), err.error_args[0])
2883
elif err.error_verb == 'nobranch':
2884
if len(err.error_args) >= 1:
2885
extra = err.error_args[0]
2888
raise errors.NotBranchError(path=find('bzrdir').root_transport.base,
2890
elif err.error_verb == 'norepository':
2891
raise errors.NoRepositoryPresent(find('bzrdir'))
2892
elif err.error_verb == 'LockContention':
2893
raise errors.LockContention('(remote lock)')
2894
elif err.error_verb == 'UnlockableTransport':
2895
raise errors.UnlockableTransport(find('bzrdir').root_transport)
2896
elif err.error_verb == 'LockFailed':
2897
raise errors.LockFailed(err.error_args[0], err.error_args[1])
2898
elif err.error_verb == 'TokenMismatch':
2899
raise errors.TokenMismatch(find('token'), '(remote token)')
2900
elif err.error_verb == 'Diverged':
2901
raise errors.DivergedBranches(find('branch'), find('other_branch'))
2902
elif err.error_verb == 'TipChangeRejected':
2903
raise errors.TipChangeRejected(err.error_args[0].decode('utf8'))
2904
elif err.error_verb == 'UnstackableBranchFormat':
2905
raise errors.UnstackableBranchFormat(*err.error_args)
2906
elif err.error_verb == 'UnstackableRepositoryFormat':
2907
raise errors.UnstackableRepositoryFormat(*err.error_args)
2908
elif err.error_verb == 'NotStacked':
2909
raise errors.NotStacked(branch=find('branch'))
2910
elif err.error_verb == 'PermissionDenied':
2912
if len(err.error_args) >= 2:
2913
extra = err.error_args[1]
2916
raise errors.PermissionDenied(path, extra=extra)
2917
elif err.error_verb == 'ReadError':
2919
raise errors.ReadError(path)
2920
elif err.error_verb == 'NoSuchFile':
2922
raise errors.NoSuchFile(path)
2923
elif err.error_verb == 'FileExists':
2924
raise errors.FileExists(err.error_args[0])
2925
elif err.error_verb == 'DirectoryNotEmpty':
2926
raise errors.DirectoryNotEmpty(err.error_args[0])
2927
elif err.error_verb == 'ShortReadvError':
2928
args = err.error_args
2929
raise errors.ShortReadvError(
2930
args[0], int(args[1]), int(args[2]), int(args[3]))
2931
elif err.error_verb in ('UnicodeEncodeError', 'UnicodeDecodeError'):
2932
encoding = str(err.error_args[0]) # encoding must always be a string
2933
val = err.error_args[1]
2934
start = int(err.error_args[2])
2935
end = int(err.error_args[3])
2936
reason = str(err.error_args[4]) # reason must always be a string
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)
4411
if not isinstance(err.error_verb, bytes):
4412
raise TypeError(err.error_verb)
4414
translator = error_translators.get(err.error_verb)
4418
raise translator(err, find, get_path)
4420
translator = no_context_error_translators.get(err.error_verb)
4422
raise errors.UnknownErrorFromSmartServer(err)
4424
raise translator(err)
4427
error_translators.register(b'NoSuchRevision',
4428
lambda err, find, get_path: NoSuchRevision(
4429
find('branch'), err.error_args[0]))
4430
error_translators.register(b'nosuchrevision',
4431
lambda err, find, get_path: NoSuchRevision(
4432
find('repository'), err.error_args[0]))
4433
error_translators.register(
4434
b'revno-outofbounds',
4435
lambda err, find, get_path: errors.RevnoOutOfBounds(
4436
err.error_args[0], (err.error_args[1], err.error_args[2])))
4439
def _translate_nobranch_error(err, find, get_path):
4440
if len(err.error_args) >= 1:
4441
extra = err.error_args[0].decode('utf-8')
4444
return errors.NotBranchError(path=find('bzrdir').root_transport.base,
4448
error_translators.register(b'nobranch', _translate_nobranch_error)
4449
error_translators.register(b'norepository',
4450
lambda err, find, get_path: errors.NoRepositoryPresent(
4452
error_translators.register(b'UnlockableTransport',
4453
lambda err, find, get_path: errors.UnlockableTransport(
4454
find('bzrdir').root_transport))
4455
error_translators.register(b'TokenMismatch',
4456
lambda err, find, get_path: errors.TokenMismatch(
4457
find('token'), '(remote token)'))
4458
error_translators.register(b'Diverged',
4459
lambda err, find, get_path: errors.DivergedBranches(
4460
find('branch'), find('other_branch')))
4461
error_translators.register(b'NotStacked',
4462
lambda err, find, get_path: errors.NotStacked(branch=find('branch')))
4465
def _translate_PermissionDenied(err, find, get_path):
4467
if len(err.error_args) >= 2:
4468
extra = err.error_args[1].decode('utf-8')
4471
return errors.PermissionDenied(path, extra=extra)
4474
error_translators.register(b'PermissionDenied', _translate_PermissionDenied)
4475
error_translators.register(b'ReadError',
4476
lambda err, find, get_path: errors.ReadError(get_path()))
4477
error_translators.register(b'NoSuchFile',
4478
lambda err, find, get_path: errors.NoSuchFile(get_path()))
4479
error_translators.register(b'TokenLockingNotSupported',
4480
lambda err, find, get_path: errors.TokenLockingNotSupported(
4481
find('repository')))
4482
error_translators.register(b'UnsuspendableWriteGroup',
4483
lambda err, find, get_path: errors.UnsuspendableWriteGroup(
4484
repository=find('repository')))
4485
error_translators.register(b'UnresumableWriteGroup',
4486
lambda err, find, get_path: errors.UnresumableWriteGroup(
4487
repository=find('repository'), write_groups=err.error_args[0],
4488
reason=err.error_args[1]))
4489
error_translators.register(b'AlreadyControlDir',
4490
lambda err, find, get_path: errors.AlreadyControlDirError(get_path()))
4492
no_context_error_translators.register(b'GhostRevisionsHaveNoRevno',
4493
lambda err: errors.GhostRevisionsHaveNoRevno(*err.error_args))
4494
no_context_error_translators.register(b'IncompatibleRepositories',
4495
lambda err: errors.IncompatibleRepositories(
4496
err.error_args[0].decode('utf-8'), err.error_args[1].decode('utf-8'), err.error_args[2].decode('utf-8')))
4497
no_context_error_translators.register(b'LockContention',
4498
lambda err: errors.LockContention('(remote lock)'))
4499
no_context_error_translators.register(b'LockFailed',
4500
lambda err: errors.LockFailed(err.error_args[0].decode('utf-8'), err.error_args[1].decode('utf-8')))
4501
no_context_error_translators.register(b'TipChangeRejected',
4502
lambda err: errors.TipChangeRejected(err.error_args[0].decode('utf8')))
4503
no_context_error_translators.register(b'UnstackableBranchFormat',
4504
lambda err: branch.UnstackableBranchFormat(*err.error_args))
4505
no_context_error_translators.register(b'UnstackableRepositoryFormat',
4506
lambda err: errors.UnstackableRepositoryFormat(*err.error_args))
4507
no_context_error_translators.register(b'FileExists',
4508
lambda err: errors.FileExists(err.error_args[0].decode('utf-8')))
4509
no_context_error_translators.register(b'DirectoryNotEmpty',
4510
lambda err: errors.DirectoryNotEmpty(err.error_args[0].decode('utf-8')))
4511
no_context_error_translators.register(b'UnknownFormat',
4512
lambda err: errors.UnknownFormatError(
4513
err.error_args[0].decode('ascii'), err.error_args[0].decode('ascii')))
4514
no_context_error_translators.register(b'InvalidURL',
4515
lambda err: urlutils.InvalidURL(
4516
err.error_args[0].decode('utf-8'), err.error_args[1].decode('ascii')))
4519
def _translate_short_readv_error(err):
4520
args = err.error_args
4521
return errors.ShortReadvError(
4522
args[0].decode('utf-8'),
4523
int(args[1].decode('ascii')), int(args[2].decode('ascii')),
4524
int(args[3].decode('ascii')))
4527
no_context_error_translators.register(b'ShortReadvError',
4528
_translate_short_readv_error)
4531
def _translate_unicode_error(err):
4532
encoding = err.error_args[0].decode('ascii')
4533
val = err.error_args[1].decode('utf-8')
4534
start = int(err.error_args[2].decode('ascii'))
4535
end = int(err.error_args[3].decode('ascii'))
4536
reason = err.error_args[4].decode('utf-8')
4537
if val.startswith('u:'):
4538
val = val[2:].decode('utf-8')
4539
elif val.startswith('s:'):
4540
val = val[2:].decode('base64')
4541
if err.error_verb == 'UnicodeDecodeError':
4542
raise UnicodeDecodeError(encoding, val, start, end, reason)
4543
elif err.error_verb == 'UnicodeEncodeError':
4544
raise UnicodeEncodeError(encoding, val, start, end, reason)
4547
no_context_error_translators.register(b'UnicodeEncodeError',
4548
_translate_unicode_error)
4549
no_context_error_translators.register(b'UnicodeDecodeError',
4550
_translate_unicode_error)
4551
no_context_error_translators.register(b'ReadOnlyError',
4552
lambda err: errors.TransportNotPossible('readonly transport'))
4553
no_context_error_translators.register(b'MemoryError',
4554
lambda err: errors.BzrError("remote server out of memory\n"
4555
"Retry non-remotely, or contact the server admin for details."))
4556
no_context_error_translators.register(b'RevisionNotPresent',
4557
lambda err: errors.RevisionNotPresent(err.error_args[0].decode('utf-8'), err.error_args[1].decode('utf-8')))
4559
no_context_error_translators.register(b'BzrCheckError',
4560
lambda err: errors.BzrCheckError(msg=err.error_args[0].decode('utf-8')))