73
105
return self._client.call_with_body_bytes_expecting_body(
74
106
method, args, body_bytes)
75
except errors.ErrorFromSmartServer, err:
107
except errors.ErrorFromSmartServer as err:
76
108
self._translate_error(err, **err_context)
79
111
def response_tuple_to_repo_format(response):
80
112
"""Convert a response tuple describing a repository format to a format."""
81
113
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')
114
format._rich_root_data = (response[0] == b'yes')
115
format._supports_tree_reference = (response[1] == b'yes')
116
format._supports_external_lookups = (response[2] == b'yes')
85
117
format._network_name = response[3]
89
# Note: RemoteBzrDirFormat is in bzrdir.py
91
class RemoteBzrDir(BzrDir, _RpcHelper):
121
# Note that RemoteBzrDirProber lives in breezy.bzrdir so breezy.bzr.remote
122
# does not have to be imported unless a remote format is involved.
124
class RemoteBzrDirFormat(_mod_bzrdir.BzrDirMetaFormat1):
125
"""Format representing bzrdirs accessed via a smart server"""
127
supports_workingtrees = False
129
colocated_branches = False
132
_mod_bzrdir.BzrDirMetaFormat1.__init__(self)
133
# XXX: It's a bit ugly that the network name is here, because we'd
134
# like to believe that format objects are stateless or at least
135
# immutable, However, we do at least avoid mutating the name after
136
# it's returned. See <https://bugs.launchpad.net/bzr/+bug/504102>
137
self._network_name = None
140
return "%s(_network_name=%r)" % (self.__class__.__name__,
143
def get_format_description(self):
144
if self._network_name:
146
real_format = controldir.network_format_registry.get(
151
return 'Remote: ' + real_format.get_format_description()
152
return 'bzr remote bzrdir'
154
def get_format_string(self):
155
raise NotImplementedError(self.get_format_string)
157
def network_name(self):
158
if self._network_name:
159
return self._network_name
161
raise AssertionError("No network name set.")
163
def initialize_on_transport(self, transport):
165
# hand off the request to the smart server
166
client_medium = transport.get_smart_medium()
167
except errors.NoSmartMedium:
168
# TODO: lookup the local format from a server hint.
169
local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
170
return local_dir_format.initialize_on_transport(transport)
171
client = _SmartClient(client_medium)
172
path = client.remote_path_from_transport(transport)
174
response = client.call(b'BzrDirFormat.initialize', path)
175
except errors.ErrorFromSmartServer as err:
176
_translate_error(err, path=path)
177
if response[0] != b'ok':
178
raise errors.SmartProtocolError(
179
'unexpected response code %s' % (response,))
180
format = RemoteBzrDirFormat()
181
self._supply_sub_formats_to(format)
182
return RemoteBzrDir(transport, format)
184
def parse_NoneTrueFalse(self, arg):
191
raise AssertionError("invalid arg %r" % arg)
193
def _serialize_NoneTrueFalse(self, arg):
200
def _serialize_NoneString(self, arg):
203
def initialize_on_transport_ex(self, transport, use_existing_dir=False,
204
create_prefix=False, force_new_repo=False, stacked_on=None,
205
stack_on_pwd=None, repo_format_name=None, make_working_trees=None,
208
# hand off the request to the smart server
209
client_medium = transport.get_smart_medium()
210
except errors.NoSmartMedium:
213
# Decline to open it if the server doesn't support our required
214
# version (3) so that the VFS-based transport will do it.
215
if client_medium.should_probe():
217
server_version = client_medium.protocol_version()
218
if server_version != '2':
222
except errors.SmartProtocolError:
223
# Apparently there's no usable smart server there, even though
224
# the medium supports the smart protocol.
229
client = _SmartClient(client_medium)
230
path = client.remote_path_from_transport(transport)
231
if client_medium._is_remote_before((1, 16)):
234
# TODO: lookup the local format from a server hint.
235
local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
236
self._supply_sub_formats_to(local_dir_format)
237
return local_dir_format.initialize_on_transport_ex(transport,
238
use_existing_dir=use_existing_dir, create_prefix=create_prefix,
239
force_new_repo=force_new_repo, stacked_on=stacked_on,
240
stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
241
make_working_trees=make_working_trees, shared_repo=shared_repo,
243
return self._initialize_on_transport_ex_rpc(client, path, transport,
244
use_existing_dir, create_prefix, force_new_repo, stacked_on,
245
stack_on_pwd, repo_format_name, make_working_trees, shared_repo)
247
def _initialize_on_transport_ex_rpc(self, client, path, transport,
248
use_existing_dir, create_prefix, force_new_repo, stacked_on,
249
stack_on_pwd, repo_format_name, make_working_trees, shared_repo):
251
args.append(self._serialize_NoneTrueFalse(use_existing_dir))
252
args.append(self._serialize_NoneTrueFalse(create_prefix))
253
args.append(self._serialize_NoneTrueFalse(force_new_repo))
254
args.append(self._serialize_NoneString(stacked_on))
255
# stack_on_pwd is often/usually our transport
258
stack_on_pwd = transport.relpath(stack_on_pwd).encode('utf-8')
261
except errors.PathNotChild:
263
args.append(self._serialize_NoneString(stack_on_pwd))
264
args.append(self._serialize_NoneString(repo_format_name))
265
args.append(self._serialize_NoneTrueFalse(make_working_trees))
266
args.append(self._serialize_NoneTrueFalse(shared_repo))
267
request_network_name = self._network_name or \
268
_mod_bzrdir.BzrDirFormat.get_default_format().network_name()
270
response = client.call(b'BzrDirFormat.initialize_ex_1.16',
271
request_network_name, path, *args)
272
except errors.UnknownSmartMethod:
273
client._medium._remember_remote_is_before((1, 16))
274
local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
275
self._supply_sub_formats_to(local_dir_format)
276
return local_dir_format.initialize_on_transport_ex(transport,
277
use_existing_dir=use_existing_dir, create_prefix=create_prefix,
278
force_new_repo=force_new_repo, stacked_on=stacked_on,
279
stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
280
make_working_trees=make_working_trees, shared_repo=shared_repo,
282
except errors.ErrorFromSmartServer as err:
283
_translate_error(err, path=path.decode('utf-8'))
284
repo_path = response[0]
285
bzrdir_name = response[6]
286
require_stacking = response[7]
287
require_stacking = self.parse_NoneTrueFalse(require_stacking)
288
format = RemoteBzrDirFormat()
289
format._network_name = bzrdir_name
290
self._supply_sub_formats_to(format)
291
bzrdir = RemoteBzrDir(transport, format, _client=client)
293
repo_format = response_tuple_to_repo_format(response[1:])
294
if repo_path == b'.':
296
repo_path = repo_path.decode('utf-8')
298
repo_bzrdir_format = RemoteBzrDirFormat()
299
repo_bzrdir_format._network_name = response[5]
300
repo_bzr = RemoteBzrDir(transport.clone(repo_path),
304
final_stack = response[8] or None
306
final_stack = final_stack.decode('utf-8')
307
final_stack_pwd = response[9] or None
309
final_stack_pwd = urlutils.join(
310
transport.base, final_stack_pwd.decode('utf-8'))
311
remote_repo = RemoteRepository(repo_bzr, repo_format)
312
if len(response) > 10:
313
# Updated server verb that locks remotely.
314
repo_lock_token = response[10] or None
315
remote_repo.lock_write(repo_lock_token, _skip_rpc=True)
317
remote_repo.dont_leave_lock_in_place()
319
remote_repo.lock_write()
320
policy = _mod_bzrdir.UseExistingRepository(remote_repo,
321
final_stack, final_stack_pwd, require_stacking)
322
policy.acquire_repository()
326
bzrdir._format.set_branch_format(self.get_branch_format())
328
# The repo has already been created, but we need to make sure that
329
# we'll make a stackable branch.
330
bzrdir._format.require_stacking(_skip_repo=True)
331
return remote_repo, bzrdir, require_stacking, policy
333
def _open(self, transport):
334
return RemoteBzrDir(transport, self)
336
def __eq__(self, other):
337
if not isinstance(other, RemoteBzrDirFormat):
339
return self.get_format_description() == other.get_format_description()
341
def __return_repository_format(self):
342
# Always return a RemoteRepositoryFormat object, but if a specific bzr
343
# repository format has been asked for, tell the RemoteRepositoryFormat
344
# that it should use that for init() etc.
345
result = RemoteRepositoryFormat()
346
custom_format = getattr(self, '_repository_format', None)
348
if isinstance(custom_format, RemoteRepositoryFormat):
351
# We will use the custom format to create repositories over the
352
# wire; expose its details like rich_root_data for code to
354
result._custom_format = custom_format
357
def get_branch_format(self):
358
result = _mod_bzrdir.BzrDirMetaFormat1.get_branch_format(self)
359
if not isinstance(result, RemoteBranchFormat):
360
new_result = RemoteBranchFormat()
361
new_result._custom_format = result
363
self.set_branch_format(new_result)
367
repository_format = property(__return_repository_format,
368
_mod_bzrdir.BzrDirMetaFormat1._set_repository_format) # .im_func)
371
class RemoteControlStore(_mod_config.IniFileStore):
372
"""Control store which attempts to use HPSS calls to retrieve control store.
374
Note that this is specific to bzr-based formats.
377
def __init__(self, bzrdir):
378
super(RemoteControlStore, self).__init__()
379
self.controldir = bzrdir
380
self._real_store = None
382
def lock_write(self, token=None):
384
return self._real_store.lock_write(token)
388
return self._real_store.unlock()
391
with self.lock_write():
392
# We need to be able to override the undecorated implementation
393
self.save_without_locking()
395
def save_without_locking(self):
396
super(RemoteControlStore, self).save()
398
def _ensure_real(self):
399
self.controldir._ensure_real()
400
if self._real_store is None:
401
self._real_store = _mod_config.ControlStore(self.controldir)
403
def external_url(self):
404
return urlutils.join(self.branch.user_url, 'control.conf')
406
def _load_content(self):
407
medium = self.controldir._client._medium
408
path = self.controldir._path_for_remote_call(self.controldir._client)
410
response, handler = self.controldir._call_expecting_body(
411
b'BzrDir.get_config_file', path)
412
except errors.UnknownSmartMethod:
414
return self._real_store._load_content()
415
if len(response) and response[0] != b'ok':
416
raise errors.UnexpectedSmartServerResponse(response)
417
return handler.read_body_bytes()
419
def _save_content(self, content):
420
# FIXME JRV 2011-11-22: Ideally this should use a
421
# HPSS call too, but at the moment it is not possible
422
# to write lock control directories.
424
return self._real_store._save_content(content)
427
class RemoteBzrDir(_mod_bzrdir.BzrDir, _RpcHelper):
92
428
"""Control directory on a remote server, accessed via bzr:// or similar."""
94
430
def __init__(self, transport, format, _client=None, _force_probe=False):
186
564
medium = self._client._medium
187
565
if medium._is_remote_before((1, 13)):
188
566
return self._vfs_cloning_metadir(require_stacking=require_stacking)
189
verb = 'BzrDir.cloning_metadir'
567
verb = b'BzrDir.cloning_metadir'
190
568
if require_stacking:
194
572
path = self._path_for_remote_call(self._client)
196
574
response = self._call(verb, path, stacking)
197
575
except errors.UnknownSmartMethod:
198
576
medium._remember_remote_is_before((1, 13))
199
577
return self._vfs_cloning_metadir(require_stacking=require_stacking)
200
except errors.UnknownErrorFromSmartServer, err:
201
if err.error_tuple != ('BranchReference',):
578
except errors.UnknownErrorFromSmartServer as err:
579
if err.error_tuple != (b'BranchReference',):
203
581
# We need to resolve the branch reference to determine the
204
582
# cloning_metadir. This causes unnecessary RPCs to open the
205
583
# referenced branch (and bzrdir, etc) but only when the caller
206
584
# didn't already resolve the branch reference.
207
585
referenced_branch = self.open_branch()
208
return referenced_branch.bzrdir.cloning_metadir()
586
return referenced_branch.controldir.cloning_metadir()
209
587
if len(response) != 3:
210
588
raise errors.UnexpectedSmartServerResponse(response)
211
589
control_name, repo_name, branch_info = response
212
590
if len(branch_info) != 2:
213
591
raise errors.UnexpectedSmartServerResponse(response)
214
592
branch_ref, branch_name = branch_info
215
format = bzrdir.network_format_registry.get(control_name)
594
format = controldir.network_format_registry.get(control_name)
596
raise errors.UnknownFormatError(
597
kind='control', format=control_name)
217
format.repository_format = repository.network_format_registry.get(
219
if branch_ref == 'ref':
601
format.repository_format = _mod_repository.network_format_registry.get(
604
raise errors.UnknownFormatError(kind='repository',
606
if branch_ref == b'ref':
220
607
# XXX: we need possible_transports here to avoid reopening the
221
608
# connection to the referenced location
222
ref_bzrdir = BzrDir.open(branch_name)
609
ref_bzrdir = _mod_bzrdir.BzrDir.open(branch_name)
223
610
branch_format = ref_bzrdir.cloning_metadir().get_branch_format()
224
611
format.set_branch_format(branch_format)
225
elif branch_ref == 'branch':
612
elif branch_ref == b'branch':
227
format.set_branch_format(
228
branch.network_format_registry.get(branch_name))
615
branch_format = branch.network_format_registry.get(
618
raise errors.UnknownFormatError(kind='branch',
620
format.set_branch_format(branch_format)
230
622
raise errors.UnexpectedSmartServerResponse(response)
266
675
def destroy_branch(self, name=None):
267
676
"""See BzrDir.destroy_branch"""
269
self._real_bzrdir.destroy_branch(name=name)
678
name = self._get_selected_branch()
680
raise errors.NoColocatedBranchSupport(self)
681
path = self._path_for_remote_call(self._client)
687
response = self._call(b'BzrDir.destroy_branch', path, *args)
688
except errors.UnknownSmartMethod:
690
self._real_bzrdir.destroy_branch(name=name)
691
self._next_open_branch_result = None
270
693
self._next_open_branch_result = None
694
if response[0] != b'ok':
695
raise SmartProtocolError(
696
'unexpected response code %s' % (response,))
272
def create_workingtree(self, revision_id=None, from_branch=None):
698
def create_workingtree(self, revision_id=None, from_branch=None,
699
accelerator_tree=None, hardlink=False):
273
700
raise errors.NotLocalUrl(self.transport.base)
275
def find_branch_format(self):
702
def find_branch_format(self, name=None):
276
703
"""Find the branch 'format' for this bzrdir.
278
705
This might be a synthetic object for e.g. RemoteBranch and SVN.
280
b = self.open_branch()
707
b = self.open_branch(name=name)
283
def get_branch_reference(self):
710
def get_branches(self, possible_transports=None, ignore_fallbacks=False):
711
path = self._path_for_remote_call(self._client)
713
response, handler = self._call_expecting_body(
714
b'BzrDir.get_branches', path)
715
except errors.UnknownSmartMethod:
717
return self._real_bzrdir.get_branches()
718
if response[0] != b"success":
719
raise errors.UnexpectedSmartServerResponse(response)
720
body = bencode.bdecode(handler.read_body_bytes())
722
for name, value in viewitems(body):
723
name = name.decode('utf-8')
724
ret[name] = self._open_branch(name, value[0], value[1],
725
possible_transports=possible_transports,
726
ignore_fallbacks=ignore_fallbacks)
729
def set_branch_reference(self, target_branch, name=None):
730
"""See BzrDir.set_branch_reference()."""
732
name = self._get_selected_branch()
734
raise errors.NoColocatedBranchSupport(self)
736
return self._real_bzrdir.set_branch_reference(target_branch, name=name)
738
def get_branch_reference(self, name=None):
284
739
"""See BzrDir.get_branch_reference()."""
741
name = self._get_selected_branch()
743
raise errors.NoColocatedBranchSupport(self)
285
744
response = self._get_branch_reference()
286
745
if response[0] == 'ref':
746
return response[1].decode('utf-8')
291
750
def _get_branch_reference(self):
751
"""Get branch reference information
753
:return: Tuple with (kind, location_or_format)
754
if kind == 'ref', then location_or_format contains a location
755
otherwise, it contains a format name
292
757
path = self._path_for_remote_call(self._client)
293
758
medium = self._client._medium
294
759
candidate_calls = [
295
('BzrDir.open_branchV3', (2, 1)),
296
('BzrDir.open_branchV2', (1, 13)),
297
('BzrDir.open_branch', None),
760
(b'BzrDir.open_branchV3', (2, 1)),
761
(b'BzrDir.open_branchV2', (1, 13)),
762
(b'BzrDir.open_branch', None),
299
764
for verb, required_version in candidate_calls:
300
765
if required_version and medium._is_remote_before(required_version):
307
772
medium._remember_remote_is_before(required_version)
310
if verb == 'BzrDir.open_branch':
311
if response[0] != 'ok':
775
if verb == b'BzrDir.open_branch':
776
if response[0] != b'ok':
312
777
raise errors.UnexpectedSmartServerResponse(response)
313
if response[1] != '':
778
if response[1] != b'':
314
779
return ('ref', response[1])
316
return ('branch', '')
317
if response[0] not in ('ref', 'branch'):
781
return ('branch', b'')
782
if response[0] not in (b'ref', b'branch'):
318
783
raise errors.UnexpectedSmartServerResponse(response)
784
return (response[0].decode('ascii'), response[1])
321
def _get_tree_branch(self):
786
def _get_tree_branch(self, name=None):
322
787
"""See BzrDir._get_tree_branch()."""
323
return None, self.open_branch()
788
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':
790
def _open_branch(self, name, kind, location_or_format,
791
ignore_fallbacks=False, possible_transports=None):
336
793
# a branch reference, use the existing BranchReference logic.
337
794
format = BranchReferenceFormat()
338
795
return format.open(self, name=name, _found=True,
339
location=response[1], ignore_fallbacks=ignore_fallbacks)
340
branch_format_name = response[1]
796
location=location_or_format.decode('utf-8'),
797
ignore_fallbacks=ignore_fallbacks,
798
possible_transports=possible_transports)
799
branch_format_name = location_or_format
341
800
if not branch_format_name:
342
801
branch_format_name = None
343
802
format = RemoteBranchFormat(network_name=branch_format_name)
344
803
return RemoteBranch(self, self.find_repository(), format=format,
345
setup_stacking=not ignore_fallbacks, name=name)
804
setup_stacking=not ignore_fallbacks, name=name,
805
possible_transports=possible_transports)
807
def open_branch(self, name=None, unsupported=False,
808
ignore_fallbacks=False, possible_transports=None):
810
name = self._get_selected_branch()
812
raise errors.NoColocatedBranchSupport(self)
814
raise NotImplementedError(
815
'unsupported flag support not implemented yet.')
816
if self._next_open_branch_result is not None:
817
# See create_branch for details.
818
result = self._next_open_branch_result
819
self._next_open_branch_result = None
821
response = self._get_branch_reference()
822
return self._open_branch(name, response[0], response[1],
823
possible_transports=possible_transports,
824
ignore_fallbacks=ignore_fallbacks)
347
826
def _open_repo_v1(self, path):
348
verb = 'BzrDir.find_repository'
827
verb = b'BzrDir.find_repository'
349
828
response = self._call(verb, path)
350
if response[0] != 'ok':
829
if response[0] != b'ok':
351
830
raise errors.UnexpectedSmartServerResponse(response)
352
831
# servers that only support the v1 method don't support external
353
832
# references either.
354
833
self._ensure_real()
355
834
repo = self._real_bzrdir.open_repository()
356
response = response + ('no', repo._format.network_name())
835
response = response + (b'no', repo._format.network_name())
357
836
return response, repo
359
838
def _open_repo_v2(self, path):
360
verb = 'BzrDir.find_repositoryV2'
839
verb = b'BzrDir.find_repositoryV2'
361
840
response = self._call(verb, path)
362
if response[0] != 'ok':
841
if response[0] != b'ok':
363
842
raise errors.UnexpectedSmartServerResponse(response)
364
843
self._ensure_real()
365
844
repo = self._real_bzrdir.open_repository()
441
937
"""Upgrading of remote bzrdirs is not supported yet."""
444
def needs_format_conversion(self, format=None):
940
def needs_format_conversion(self, format):
445
941
"""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
944
def _get_config(self):
458
945
return RemoteBzrDirConfig(self)
461
class RemoteRepositoryFormat(repository.RepositoryFormat):
947
def _get_config_store(self):
948
return RemoteControlStore(self)
951
class RemoteInventoryTree(InventoryRevisionTree):
953
def __init__(self, repository, inv, revision_id):
954
super(RemoteInventoryTree, self).__init__(repository, inv, revision_id)
956
def archive(self, format, name, root=None, subdir=None, force_mtime=None):
957
ret = self._repository._revision_archive(
958
self.get_revision_id(), format, name, root, subdir,
959
force_mtime=force_mtime)
961
return super(RemoteInventoryTree, self).archive(
962
format, name, root, subdir, force_mtime=force_mtime)
965
def annotate_iter(self, path,
966
default_revision=_mod_revision.CURRENT_REVISION):
967
"""Return an iterator of revision_id, line tuples.
969
For working trees (and mutable trees in general), the special
970
revision_id 'current:' will be used for lines that are new in this
971
tree, e.g. uncommitted changes.
972
:param default_revision: For lines that don't match a basis, mark them
973
with this revision id. Not all implementations will make use of
976
ret = self._repository._annotate_file_revision(
977
self.get_revision_id(), path, file_id=None,
978
default_revision=default_revision)
980
return super(RemoteInventoryTree, self).annotate_iter(
981
path, default_revision=default_revision)
985
class RemoteRepositoryFormat(vf_repository.VersionedFileRepositoryFormat):
462
986
"""Format for repositories accessed over a _SmartClient.
464
988
Instances of this repository are represented by RemoteRepository
529
1075
self._custom_format.supports_tree_reference
530
1076
return self._supports_tree_reference
532
def _vfs_initialize(self, a_bzrdir, shared):
1079
def revision_graph_can_have_wrong_parents(self):
1080
if self._revision_graph_can_have_wrong_parents is None:
1082
self._revision_graph_can_have_wrong_parents = \
1083
self._custom_format.revision_graph_can_have_wrong_parents
1084
return self._revision_graph_can_have_wrong_parents
1086
def _vfs_initialize(self, a_controldir, shared):
533
1087
"""Helper for common code in initialize."""
534
1088
if self._custom_format:
535
1089
# Custom format requested
536
result = self._custom_format.initialize(a_bzrdir, shared=shared)
1090
result = self._custom_format.initialize(
1091
a_controldir, shared=shared)
537
1092
elif self._creating_bzrdir is not None:
538
1093
# Use the format that the repository we were created to back
540
1095
prior_repo = self._creating_bzrdir.open_repository()
541
1096
prior_repo._ensure_real()
542
1097
result = prior_repo._real_repository._format.initialize(
543
a_bzrdir, shared=shared)
1098
a_controldir, shared=shared)
545
1100
# assume that a_bzr is a RemoteBzrDir but the smart server didn't
546
1101
# support remote initialization.
547
1102
# We delegate to a real object at this point (as RemoteBzrDir
548
1103
# 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)
1104
# recursion if we just called a_controldir.create_repository.
1105
a_controldir._ensure_real()
1106
result = a_controldir._real_bzrdir.create_repository(shared=shared)
552
1107
if not isinstance(result, RemoteRepository):
553
return self.open(a_bzrdir)
1108
return self.open(a_controldir)
557
def initialize(self, a_bzrdir, shared=False):
1112
def initialize(self, a_controldir, shared=False):
558
1113
# 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
1114
if not isinstance(a_controldir, RemoteBzrDir):
1115
return self._vfs_initialize(a_controldir, shared)
1116
medium = a_controldir._client._medium
562
1117
if medium._is_remote_before((1, 13)):
563
return self._vfs_initialize(a_bzrdir, shared)
1118
return self._vfs_initialize(a_controldir, shared)
564
1119
# Creating on a remote bzr dir.
565
1120
# 1) get the network name to use.
566
1121
if self._custom_format:
568
1123
elif self._network_name:
569
1124
network_name = self._network_name
571
# Select the current bzrlib default and ask for that.
572
reference_bzrdir_format = bzrdir.format_registry.get('default')()
1126
# Select the current breezy default and ask for that.
1127
reference_bzrdir_format = controldir.format_registry.get(
573
1129
reference_format = reference_bzrdir_format.repository_format
574
1130
network_name = reference_format.network_name()
575
1131
# 2) try direct creation via RPC
576
path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
577
verb = 'BzrDir.create_repository'
1132
path = a_controldir._path_for_remote_call(a_controldir._client)
1133
verb = b'BzrDir.create_repository'
1135
shared_str = b'True'
1137
shared_str = b'False'
583
response = a_bzrdir._call(verb, path, network_name, shared_str)
1139
response = a_controldir._call(verb, path, network_name, shared_str)
584
1140
except errors.UnknownSmartMethod:
585
1141
# Fallback - use vfs methods
586
1142
medium._remember_remote_is_before((1, 13))
587
return self._vfs_initialize(a_bzrdir, shared)
1143
return self._vfs_initialize(a_controldir, shared)
589
1145
# Turn the response into a RemoteRepository object.
590
1146
format = response_tuple_to_repo_format(response[1:])
591
1147
# Used to support creating a real format instance when needed.
592
format._creating_bzrdir = a_bzrdir
593
remote_repo = RemoteRepository(a_bzrdir, format)
1148
format._creating_bzrdir = a_controldir
1149
remote_repo = RemoteRepository(a_controldir, format)
594
1150
format._creating_repo = remote_repo
595
1151
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()
1153
def open(self, a_controldir):
1154
if not isinstance(a_controldir, RemoteBzrDir):
1155
raise AssertionError('%r is not a RemoteBzrDir' % (a_controldir,))
1156
return a_controldir.open_repository()
602
1158
def _ensure_real(self):
603
1159
if self._custom_format is None:
604
self._custom_format = repository.network_format_registry.get(
1161
self._custom_format = _mod_repository.network_format_registry.get(
1164
raise errors.UnknownFormatError(kind='repository',
1165
format=self._network_name)
608
1168
def _fetch_order(self):
909
1532
# TODO: Move to RepositoryBase and unify with the regular Repository
910
1533
# one; unfortunately the tests rely on slightly different behaviour at
911
1534
# present -- mbp 20090710
912
return (self.__class__ is other.__class__ and
913
self.bzrdir.transport.base == other.bzrdir.transport.base)
1535
return (self.__class__ is other.__class__
1536
and self.controldir.transport.base == other.controldir.transport.base)
915
1538
def get_graph(self, other_repository=None):
916
1539
"""Return the graph for this repository format"""
917
1540
parents_provider = self._make_parents_provider(other_repository)
918
1541
return graph.Graph(parents_provider)
921
1543
def get_known_graph_ancestry(self, revision_ids):
922
1544
"""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)
1546
with self.lock_read():
1547
revision_graph = dict(((key, value) for key, value in
1548
self.get_graph().iter_ancestry(revision_ids) if value is not None))
1549
revision_graph = _mod_repository._strip_NULL_ghosts(revision_graph)
1550
return graph.KnownGraph(revision_graph)
929
1552
def gather_stats(self, revid=None, committers=None):
930
1553
"""See Repository.gather_stats()."""
931
path = self.bzrdir._path_for_remote_call(self._client)
1554
path = self.controldir._path_for_remote_call(self._client)
932
1555
# revid can be None to indicate no revisions, not just NULL_REVISION
933
if revid is None or revision.is_null(revid):
1556
if revid is None or _mod_revision.is_null(revid):
936
1559
fmt_revid = revid
937
1560
if committers is None or not committers:
938
fmt_committers = 'no'
1561
fmt_committers = b'no'
940
fmt_committers = 'yes'
1563
fmt_committers = b'yes'
941
1564
response_tuple, response_handler = self._call_expecting_body(
942
'Repository.gather_stats', path, fmt_revid, fmt_committers)
943
if response_tuple[0] != 'ok':
1565
b'Repository.gather_stats', path, fmt_revid, fmt_committers)
1566
if response_tuple[0] != b'ok':
944
1567
raise errors.UnexpectedSmartServerResponse(response_tuple)
946
1569
body = response_handler.read_body_bytes()
948
for line in body.split('\n'):
1571
for line in body.split(b'\n'):
951
key, val_text = line.split(':')
1574
key, val_text = line.split(b':')
1575
key = key.decode('ascii')
952
1576
if key in ('revisions', 'size', 'committers'):
953
1577
result[key] = int(val_text)
954
1578
elif key in ('firstrev', 'latestrev'):
955
values = val_text.split(' ')[1:]
956
result[key] = (float(values[0]), long(values[1]))
1579
values = val_text.split(b' ')[1:]
1580
result[key] = (float(values[0]), int(values[1]))
1195
1861
raise errors.UnexpectedSmartServerResponse(response)
1197
1863
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)
1864
"""Create a descendent repository for new development.
1866
Unlike clone, this does not copy the settings of the repository.
1868
with self.lock_read():
1869
dest_repo = self._create_sprouting_repo(to_bzrdir, shared=False)
1870
dest_repo.fetch(self, revision_id=revision_id)
1873
def _create_sprouting_repo(self, a_controldir, shared):
1874
if not isinstance(a_controldir._format, self.controldir._format.__class__):
1875
# use target default format.
1876
dest_repo = a_controldir.create_repository()
1878
# Most control formats need the repository to be specifically
1879
# created, but on some old all-in-one formats it's not needed
1881
dest_repo = self._format.initialize(
1882
a_controldir, shared=shared)
1883
except errors.UninitializableFormat:
1884
dest_repo = a_controldir.open_repository()
1203
1885
return dest_repo
1205
### These methods are just thin shims to the VFS object for now.
1887
# These methods are just thin shims to the VFS object for now.
1207
1889
def revision_tree(self, revision_id):
1209
return self._real_repository.revision_tree(revision_id)
1890
with self.lock_read():
1891
revision_id = _mod_revision.ensure_null(revision_id)
1892
if revision_id == _mod_revision.NULL_REVISION:
1893
return InventoryRevisionTree(self,
1894
Inventory(root_id=None), _mod_revision.NULL_REVISION)
1896
return list(self.revision_trees([revision_id]))[0]
1211
1898
def get_serializer_format(self):
1213
return self._real_repository.get_serializer_format()
1899
path = self.controldir._path_for_remote_call(self._client)
1901
response = self._call(b'VersionedFileRepository.get_serializer_format',
1903
except errors.UnknownSmartMethod:
1905
return self._real_repository.get_serializer_format()
1906
if response[0] != b'ok':
1907
raise errors.UnexpectedSmartServerResponse(response)
1215
1910
def get_commit_builder(self, branch, parents, config, timestamp=None,
1216
1911
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)
1912
revision_id=None, lossy=False):
1913
"""Obtain a CommitBuilder for this repository.
1915
:param branch: Branch to commit to.
1916
:param parents: Revision ids of the parents of the new revision.
1917
:param config: Configuration to use.
1918
:param timestamp: Optional timestamp recorded for commit.
1919
:param timezone: Optional timezone for timestamp.
1920
:param committer: Optional committer to set for commit.
1921
:param revprops: Optional dictionary of revision properties.
1922
:param revision_id: Optional revision id.
1923
:param lossy: Whether to discard data that can not be natively
1924
represented, when pushing to a foreign VCS
1926
if self._fallback_repositories and not self._format.supports_chks:
1927
raise errors.BzrError("Cannot commit directly to a stacked branch"
1928
" in pre-2a formats. See "
1929
"https://bugs.launchpad.net/bzr/+bug/375013 for details.")
1930
commit_builder_kls = vf_repository.VersionedFileCommitBuilder
1931
result = commit_builder_kls(self, parents, config,
1932
timestamp, timezone, committer, revprops, revision_id,
1934
self.start_write_group()
1227
1937
def add_fallback_repository(self, repository):
1228
1938
"""Add a repository to use for looking up data not held locally.
1266
1977
return self._real_repository.add_inventory(revid, inv, parents)
1268
1979
def add_inventory_by_delta(self, basis_revision_id, delta, new_revision_id,
1269
parents, basis_inv=None, propagate_caches=False):
1980
parents, basis_inv=None, propagate_caches=False):
1270
1981
self._ensure_real()
1271
1982
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)
1983
delta, new_revision_id, parents, basis_inv=basis_inv,
1984
propagate_caches=propagate_caches)
1986
def add_revision(self, revision_id, rev, inv=None):
1987
_mod_revision.check_not_reserved_id(revision_id)
1988
key = (revision_id,)
1989
# check inventory present
1990
if not self.inventories.get_parent_map([key]):
1992
raise errors.WeaveRevisionNotPresent(revision_id,
1995
# yes, this is not suitable for adding with ghosts.
1996
rev.inventory_sha1 = self.add_inventory(revision_id, inv,
1999
rev.inventory_sha1 = self.inventories.get_sha1s([key])[key]
2000
self._add_revision(rev)
2002
def _add_revision(self, rev):
2003
if self._real_repository is not None:
2004
return self._real_repository._add_revision(rev)
2005
text = self._serializer.write_revision_to_string(rev)
2006
key = (rev.revision_id,)
2007
parents = tuple((parent,) for parent in rev.parent_ids)
2008
self._write_group_tokens, missing_keys = self._get_sink().insert_stream(
2009
[('revisions', [FulltextContentFactory(key, parents, None, text)])],
2010
self._format, self._write_group_tokens)
1281
2012
def get_inventory(self, revision_id):
2013
with self.lock_read():
2014
return list(self.iter_inventories([revision_id]))[0]
2016
def _iter_inventories_rpc(self, revision_ids, ordering):
2017
if ordering is None:
2018
ordering = 'unordered'
2019
path = self.controldir._path_for_remote_call(self._client)
2020
body = b"\n".join(revision_ids)
2021
response_tuple, response_handler = (
2022
self._call_with_body_bytes_expecting_body(
2023
b"VersionedFileRepository.get_inventories",
2024
(path, ordering.encode('ascii')), body))
2025
if response_tuple[0] != b"ok":
2026
raise errors.UnexpectedSmartServerResponse(response_tuple)
2027
deserializer = inventory_delta.InventoryDeltaDeserializer()
2028
byte_stream = response_handler.read_streamed_body()
2029
decoded = smart_repo._byte_stream_to_stream(byte_stream)
2031
# no results whatsoever
2033
src_format, stream = decoded
2034
if src_format.network_name() != self._format.network_name():
2035
raise AssertionError(
2036
"Mismatched RemoteRepository and stream src %r, %r" % (
2037
src_format.network_name(), self._format.network_name()))
2038
# ignore the src format, it's not really relevant
2039
prev_inv = Inventory(root_id=None,
2040
revision_id=_mod_revision.NULL_REVISION)
2041
# there should be just one substream, with inventory deltas
2043
substream_kind, substream = next(stream)
2044
except StopIteration:
2046
if substream_kind != "inventory-deltas":
2047
raise AssertionError(
2048
"Unexpected stream %r received" % substream_kind)
2049
for record in substream:
2050
(parent_id, new_id, versioned_root, tree_references, invdelta) = (
2051
deserializer.parse_text_bytes(record.get_bytes_as("fulltext")))
2052
if parent_id != prev_inv.revision_id:
2053
raise AssertionError("invalid base %r != %r" % (parent_id,
2054
prev_inv.revision_id))
2055
inv = prev_inv.create_by_apply_delta(invdelta, new_id)
2056
yield inv, inv.revision_id
2059
def _iter_inventories_vfs(self, revision_ids, ordering=None):
1282
2060
self._ensure_real()
1283
return self._real_repository.get_inventory(revision_id)
2061
return self._real_repository._iter_inventories(revision_ids, ordering)
1285
2063
def iter_inventories(self, revision_ids, ordering=None):
1287
return self._real_repository.iter_inventories(revision_ids, ordering)
2064
"""Get many inventories by revision_ids.
2066
This will buffer some or all of the texts used in constructing the
2067
inventories in memory, but will only parse a single inventory at a
2070
:param revision_ids: The expected revision ids of the inventories.
2071
:param ordering: optional ordering, e.g. 'topological'. If not
2072
specified, the order of revision_ids will be preserved (by
2073
buffering if necessary).
2074
:return: An iterator of inventories.
2076
if ((None in revision_ids) or
2077
(_mod_revision.NULL_REVISION in revision_ids)):
2078
raise ValueError('cannot get null revision inventory')
2079
for inv, revid in self._iter_inventories(revision_ids, ordering):
2081
raise errors.NoSuchRevision(self, revid)
2084
def _iter_inventories(self, revision_ids, ordering=None):
2085
if len(revision_ids) == 0:
2087
missing = set(revision_ids)
2088
if ordering is None:
2089
order_as_requested = True
2091
order = list(revision_ids)
2093
next_revid = order.pop()
2095
order_as_requested = False
2096
if ordering != 'unordered' and self._fallback_repositories:
2097
raise ValueError('unsupported ordering %r' % ordering)
2098
iter_inv_fns = [self._iter_inventories_rpc] + [
2099
fallback._iter_inventories for fallback in
2100
self._fallback_repositories]
2102
for iter_inv in iter_inv_fns:
2103
request = [revid for revid in revision_ids if revid in missing]
2104
for inv, revid in iter_inv(request, ordering):
2107
missing.remove(inv.revision_id)
2108
if ordering != 'unordered':
2112
if order_as_requested:
2113
# Yield as many results as we can while preserving order.
2114
while next_revid in invs:
2115
inv = invs.pop(next_revid)
2116
yield inv, inv.revision_id
2118
next_revid = order.pop()
2120
# We still want to fully consume the stream, just
2121
# in case it is not actually finished at this point
2124
except errors.UnknownSmartMethod:
2125
for inv, revid in self._iter_inventories_vfs(revision_ids, ordering):
2129
if order_as_requested:
2130
if next_revid is not None:
2131
yield None, next_revid
2134
yield invs.get(revid), revid
2137
yield None, missing.pop()
1290
2139
def get_revision(self, revision_id):
1292
return self._real_repository.get_revision(revision_id)
2140
with self.lock_read():
2141
return self.get_revisions([revision_id])[0]
1294
2143
def get_transaction(self):
1295
2144
self._ensure_real()
1296
2145
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)
2147
def clone(self, a_controldir, revision_id=None):
2148
with self.lock_read():
2149
dest_repo = self._create_sprouting_repo(
2150
a_controldir, shared=self.is_shared())
2151
self.copy_content_into(dest_repo, revision_id)
1303
2154
def make_working_trees(self):
1304
2155
"""See Repository.make_working_trees"""
1306
return self._real_repository.make_working_trees()
2156
path = self.controldir._path_for_remote_call(self._client)
2158
response = self._call(b'Repository.make_working_trees', path)
2159
except errors.UnknownSmartMethod:
2161
return self._real_repository.make_working_trees()
2162
if response[0] not in (b'yes', b'no'):
2163
raise SmartProtocolError(
2164
'unexpected response code %s' % (response,))
2165
return response[0] == b'yes'
1308
2167
def refresh_data(self):
1309
"""Re-read any data needed to to synchronise with disk.
2168
"""Re-read any data needed to synchronise with disk.
1311
2170
This method is intended to be called after another repository instance
1312
2171
(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.
2172
repository. On all repositories this will work outside of write groups.
2173
Some repository formats (pack and newer for breezy native formats)
2174
support refresh_data inside write groups. If called inside a write
2175
group on a repository that does not support refreshing in a write group
2176
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
2178
if self._real_repository is not None:
1320
2179
self._real_repository.refresh_data()
2180
# Refresh the parents cache for this object
2181
self._unstacked_provider.disable_cache()
2182
self._unstacked_provider.enable_cache()
1322
2184
def revision_ids_to_search_result(self, result_set):
1323
2185
"""Convert a set of revision ids to a graph SearchResult."""
1324
2186
result_parents = set()
1325
for parents in self.get_graph().get_parent_map(
1326
result_set).itervalues():
2187
for parents in viewvalues(self.get_graph().get_parent_map(result_set)):
1327
2188
result_parents.update(parents)
1328
2189
included_keys = result_set.intersection(result_parents)
1329
2190
start_keys = result_set.difference(included_keys)
1330
2191
exclude_keys = result_parents.difference(result_set)
1331
result = graph.SearchResult(start_keys, exclude_keys,
1332
len(result_set), result_set)
2192
result = vf_search.SearchResult(start_keys, exclude_keys,
2193
len(result_set), result_set)
1336
def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
2196
def search_missing_revision_ids(self, other,
2197
find_ghosts=True, revision_ids=None, if_present_ids=None,
1337
2199
"""Return the revision ids that other has that this does not.
1339
2201
These are returned in topological order.
1341
2203
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)
2205
with self.lock_read():
2206
inter_repo = _mod_repository.InterRepository.get(other, self)
2207
return inter_repo.search_missing_revision_ids(
2208
find_ghosts=find_ghosts, revision_ids=revision_ids,
2209
if_present_ids=if_present_ids, limit=limit)
1346
def fetch(self, source, revision_id=None, pb=None, find_ghosts=False,
2211
def fetch(self, source, revision_id=None, find_ghosts=False,
1348
2213
# No base implementation to use as RemoteRepository is not a subclass
1349
2214
# of Repository; so this is a copy of Repository.fetch().
1350
2215
if fetch_spec is not None and revision_id is not None:
1388
2252
return self._real_repository._get_versioned_file_checker(
1389
2253
revisions, revision_versions_cache)
2255
def _iter_files_bytes_rpc(self, desired_files, absent):
2256
path = self.controldir._path_for_remote_call(self._client)
2259
for (file_id, revid, identifier) in desired_files:
2260
lines.append(b''.join([
2261
osutils.safe_file_id(file_id),
2263
osutils.safe_revision_id(revid)]))
2264
identifiers.append(identifier)
2265
(response_tuple, response_handler) = (
2266
self._call_with_body_bytes_expecting_body(
2267
b"Repository.iter_files_bytes", (path, ), b"\n".join(lines)))
2268
if response_tuple != (b'ok', ):
2269
response_handler.cancel_read_body()
2270
raise errors.UnexpectedSmartServerResponse(response_tuple)
2271
byte_stream = response_handler.read_streamed_body()
2273
def decompress_stream(start, byte_stream, unused):
2274
decompressor = zlib.decompressobj()
2275
yield decompressor.decompress(start)
2276
while decompressor.unused_data == b"":
2278
data = next(byte_stream)
2279
except StopIteration:
2281
yield decompressor.decompress(data)
2282
yield decompressor.flush()
2283
unused.append(decompressor.unused_data)
2286
while b"\n" not in unused:
2288
unused += next(byte_stream)
2289
except StopIteration:
2291
header, rest = unused.split(b"\n", 1)
2292
args = header.split(b"\0")
2293
if args[0] == b"absent":
2294
absent[identifiers[int(args[3])]] = (args[1], args[2])
2297
elif args[0] == b"ok":
2300
raise errors.UnexpectedSmartServerResponse(args)
2302
yield (identifiers[idx],
2303
decompress_stream(rest, byte_stream, unused_chunks))
2304
unused = b"".join(unused_chunks)
1391
2306
def iter_files_bytes(self, desired_files):
1392
2307
"""See Repository.iter_file_bytes.
1395
return self._real_repository.iter_files_bytes(desired_files)
2311
for (identifier, bytes_iterator) in self._iter_files_bytes_rpc(
2312
desired_files, absent):
2313
yield identifier, bytes_iterator
2314
for fallback in self._fallback_repositories:
2317
desired_files = [(key[0], key[1], identifier)
2318
for identifier, key in viewitems(absent)]
2319
for (identifier, bytes_iterator) in fallback.iter_files_bytes(desired_files):
2320
del absent[identifier]
2321
yield identifier, bytes_iterator
2323
# There may be more missing items, but raise an exception
2325
missing_identifier = next(iter(absent))
2326
missing_key = absent[missing_identifier]
2327
raise errors.RevisionNotPresent(revision_id=missing_key[1],
2328
file_id=missing_key[0])
2329
except errors.UnknownSmartMethod:
2331
for (identifier, bytes_iterator) in (
2332
self._real_repository.iter_files_bytes(desired_files)):
2333
yield identifier, bytes_iterator
2335
def get_cached_parent_map(self, revision_ids):
2336
"""See breezy.CachingParentsProvider.get_cached_parent_map"""
2337
return self._unstacked_provider.get_cached_parent_map(revision_ids)
1397
2339
def get_parent_map(self, revision_ids):
1398
"""See bzrlib.Graph.get_parent_map()."""
2340
"""See breezy.Graph.get_parent_map()."""
1399
2341
return self._make_parents_provider().get_parent_map(revision_ids)
1401
2343
def _get_parent_map_rpc(self, keys):
1529
2460
revision_graph[d[0]] = (NULL_REVISION,)
1530
2461
return revision_graph
1533
2463
def get_signature_text(self, revision_id):
1535
return self._real_repository.get_signature_text(revision_id)
2464
with self.lock_read():
2465
path = self.controldir._path_for_remote_call(self._client)
2467
response_tuple, response_handler = self._call_expecting_body(
2468
b'Repository.get_revision_signature_text', path, revision_id)
2469
except errors.UnknownSmartMethod:
2471
return self._real_repository.get_signature_text(revision_id)
2472
except errors.NoSuchRevision as err:
2473
for fallback in self._fallback_repositories:
2475
return fallback.get_signature_text(revision_id)
2476
except errors.NoSuchRevision:
2480
if response_tuple[0] != b'ok':
2481
raise errors.UnexpectedSmartServerResponse(response_tuple)
2482
return response_handler.read_body_bytes()
1538
2484
def _get_inventory_xml(self, revision_id):
1540
return self._real_repository._get_inventory_xml(revision_id)
2485
with self.lock_read():
2486
# This call is used by older working tree formats,
2487
# which stored a serialized basis inventory.
2489
return self._real_repository._get_inventory_xml(revision_id)
1542
2491
def reconcile(self, other=None, thorough=False):
1544
return self._real_repository.reconcile(other=other, thorough=thorough)
2492
from ..reconcile import ReconcileResult
2493
with self.lock_write():
2494
path = self.controldir._path_for_remote_call(self._client)
2496
response, handler = self._call_expecting_body(
2497
b'Repository.reconcile', path, self._lock_token)
2498
except (errors.UnknownSmartMethod, errors.TokenLockingNotSupported):
2500
return self._real_repository.reconcile(other=other, thorough=thorough)
2501
if response != (b'ok', ):
2502
raise errors.UnexpectedSmartServerResponse(response)
2503
body = handler.read_body_bytes()
2504
result = ReconcileResult()
2505
result.garbage_inventories = None
2506
result.inconsistent_parents = None
2507
result.aborted = None
2508
for line in body.split(b'\n'):
2511
key, val_text = line.split(b':')
2512
if key == b"garbage_inventories":
2513
result.garbage_inventories = int(val_text)
2514
elif key == b"inconsistent_parents":
2515
result.inconsistent_parents = int(val_text)
2517
mutter("unknown reconcile key %r" % key)
1546
2520
def all_revision_ids(self):
1548
return self._real_repository.all_revision_ids()
2521
path = self.controldir._path_for_remote_call(self._client)
2523
response_tuple, response_handler = self._call_expecting_body(
2524
b"Repository.all_revision_ids", path)
2525
except errors.UnknownSmartMethod:
2527
return self._real_repository.all_revision_ids()
2528
if response_tuple != (b"ok", ):
2529
raise errors.UnexpectedSmartServerResponse(response_tuple)
2530
revids = set(response_handler.read_body_bytes().splitlines())
2531
for fallback in self._fallback_repositories:
2532
revids.update(set(fallback.all_revision_ids()))
2535
def _filtered_revision_trees(self, revision_ids, file_ids):
2536
"""Return Tree for a revision on this branch with only some files.
2538
:param revision_ids: a sequence of revision-ids;
2539
a revision-id may not be None or b'null:'
2540
:param file_ids: if not None, the result is filtered
2541
so that only those file-ids, their parents and their
2542
children are included.
2544
inventories = self.iter_inventories(revision_ids)
2545
for inv in inventories:
2546
# Should we introduce a FilteredRevisionTree class rather
2547
# than pre-filter the inventory here?
2548
filtered_inv = inv.filter(file_ids)
2549
yield InventoryRevisionTree(self, filtered_inv, filtered_inv.revision_id)
1551
2551
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)
2552
with self.lock_read():
2553
medium = self._client._medium
2554
if medium._is_remote_before((1, 2)):
2556
for delta in self._real_repository.get_deltas_for_revisions(
2557
revisions, specific_fileids):
2560
# Get the revision-ids of interest
2561
required_trees = set()
2562
for revision in revisions:
2563
required_trees.add(revision.revision_id)
2564
required_trees.update(revision.parent_ids[:1])
2566
# Get the matching filtered trees. Note that it's more
2567
# efficient to pass filtered trees to changes_from() rather
2568
# than doing the filtering afterwards. changes_from() could
2569
# arguably do the filtering itself but it's path-based, not
2570
# file-id based, so filtering before or afterwards is
2572
if specific_fileids is None:
2573
trees = dict((t.get_revision_id(), t) for
2574
t in self.revision_trees(required_trees))
2576
trees = dict((t.get_revision_id(), t) for
2577
t in self._filtered_revision_trees(required_trees,
2580
# Calculate the deltas
2581
for revision in revisions:
2582
if not revision.parent_ids:
2583
old_tree = self.revision_tree(_mod_revision.NULL_REVISION)
2585
old_tree = trees[revision.parent_ids[0]]
2586
yield trees[revision.revision_id].changes_from(old_tree)
2588
def get_revision_delta(self, revision_id):
2589
with self.lock_read():
2590
r = self.get_revision(revision_id)
2591
return list(self.get_deltas_for_revisions([r]))[0]
1563
2593
def revision_trees(self, revision_ids):
1565
return self._real_repository.revision_trees(revision_ids)
2594
with self.lock_read():
2595
inventories = self.iter_inventories(revision_ids)
2596
for inv in inventories:
2597
yield RemoteInventoryTree(self, inv, inv.revision_id)
1568
2599
def get_revision_reconcile(self, revision_id):
1570
return self._real_repository.get_revision_reconcile(revision_id)
2600
with self.lock_read():
2602
return self._real_repository.get_revision_reconcile(revision_id)
1573
2604
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)
2605
with self.lock_read():
2607
return self._real_repository.check(revision_ids=revision_ids,
2608
callback_refs=callback_refs, check_repo=check_repo)
1578
2610
def copy_content_into(self, destination, revision_id=None):
1580
return self._real_repository.copy_content_into(
1581
destination, revision_id=revision_id)
2611
"""Make a complete copy of the content in self into destination.
2613
This is a destructive operation! Do not use it on existing
2616
interrepo = _mod_repository.InterRepository.get(self, destination)
2617
return interrepo.copy_content(revision_id)
1583
2619
def _copy_repository_tarball(self, to_bzrdir, revision_id=None):
1584
2620
# get a tarball of the remote repository, and copy from that into the
1586
from bzrlib import osutils
1588
2623
# TODO: Maybe a progress bar while streaming the tarball?
1589
note("Copying repository content as tarball...")
2624
note(gettext("Copying repository content as tarball..."))
1590
2625
tar_file = self._get_tarball('bz2')
1591
2626
if tar_file is None:
1593
2628
destination = to_bzrdir.create_repository()
1595
2630
tar = tarfile.open('repository', fileobj=tar_file,
1597
2632
tmpdir = osutils.mkdtemp()
1599
_extract_tar(tar, tmpdir)
1600
tmp_bzrdir = BzrDir.open(tmpdir)
2634
tar.extractall(tmpdir)
2635
tmp_bzrdir = _mod_bzrdir.BzrDir.open(tmpdir)
1601
2636
tmp_repo = tmp_bzrdir.open_repository()
1602
2637
tmp_repo.copy_content_into(destination, revision_id)
1680
2726
self._ensure_real()
1681
2727
return self._real_repository.texts
1684
def get_revisions(self, revision_ids):
1686
return self._real_repository.get_revisions(revision_ids)
2729
def _iter_revisions_rpc(self, revision_ids):
2730
body = b"\n".join(revision_ids)
2731
path = self.controldir._path_for_remote_call(self._client)
2732
response_tuple, response_handler = (
2733
self._call_with_body_bytes_expecting_body(
2734
b"Repository.iter_revisions", (path, ), body))
2735
if response_tuple[0] != b"ok":
2736
raise errors.UnexpectedSmartServerResponse(response_tuple)
2737
serializer_format = response_tuple[1].decode('ascii')
2738
serializer = serializer_format_registry.get(serializer_format)
2739
byte_stream = response_handler.read_streamed_body()
2740
decompressor = zlib.decompressobj()
2742
for bytes in byte_stream:
2743
chunks.append(decompressor.decompress(bytes))
2744
if decompressor.unused_data != b"":
2745
chunks.append(decompressor.flush())
2746
yield serializer.read_revision_from_string(b"".join(chunks))
2747
unused = decompressor.unused_data
2748
decompressor = zlib.decompressobj()
2749
chunks = [decompressor.decompress(unused)]
2750
chunks.append(decompressor.flush())
2751
text = b"".join(chunks)
2753
yield serializer.read_revision_from_string(b"".join(chunks))
2755
def iter_revisions(self, revision_ids):
2756
for rev_id in revision_ids:
2757
if not rev_id or not isinstance(rev_id, bytes):
2758
raise errors.InvalidRevisionId(
2759
revision_id=rev_id, branch=self)
2760
with self.lock_read():
2762
missing = set(revision_ids)
2763
for rev in self._iter_revisions_rpc(revision_ids):
2764
missing.remove(rev.revision_id)
2765
yield (rev.revision_id, rev)
2766
for fallback in self._fallback_repositories:
2769
for (revid, rev) in fallback.iter_revisions(missing):
2772
missing.remove(revid)
2773
for revid in missing:
2775
except errors.UnknownSmartMethod:
2777
for entry in self._real_repository.iter_revisions(revision_ids):
1688
2780
def supports_rich_root(self):
1689
2781
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
2784
def _serializer(self):
1697
2785
return self._format._serializer
1699
2787
def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
1701
return self._real_repository.store_revision_signature(
1702
gpg_strategy, plaintext, revision_id)
2788
with self.lock_write():
2789
signature = gpg_strategy.sign(plaintext, gpg.MODE_CLEAR)
2790
self.add_signature_text(revision_id, signature)
1704
2792
def add_signature_text(self, revision_id, signature):
1706
return self._real_repository.add_signature_text(revision_id, signature)
2793
if self._real_repository:
2794
# If there is a real repository the write group will
2795
# be in the real repository as well, so use that:
2797
return self._real_repository.add_signature_text(
2798
revision_id, signature)
2799
path = self.controldir._path_for_remote_call(self._client)
2800
response, handler = self._call_with_body_bytes_expecting_body(
2801
b'Repository.add_signature_text', (path, self._lock_token,
2803
tuple([token.encode('utf-8')
2804
for token in self._write_group_tokens]),
2806
handler.cancel_read_body()
2808
if response[0] != b'ok':
2809
raise errors.UnexpectedSmartServerResponse(response)
2810
self._write_group_tokens = [token.decode(
2811
'utf-8') for token in response[1:]]
1708
2813
def has_signature_for_revision_id(self, revision_id):
1710
return self._real_repository.has_signature_for_revision_id(revision_id)
2814
path = self.controldir._path_for_remote_call(self._client)
2816
response = self._call(b'Repository.has_signature_for_revision_id',
2818
except errors.UnknownSmartMethod:
2820
return self._real_repository.has_signature_for_revision_id(
2822
if response[0] not in (b'yes', b'no'):
2823
raise SmartProtocolError(
2824
'unexpected response code %s' % (response,))
2825
if response[0] == b'yes':
2827
for fallback in self._fallback_repositories:
2828
if fallback.has_signature_for_revision_id(revision_id):
2832
def verify_revision_signature(self, revision_id, gpg_strategy):
2833
with self.lock_read():
2834
if not self.has_signature_for_revision_id(revision_id):
2835
return gpg.SIGNATURE_NOT_SIGNED, None
2836
signature = self.get_signature_text(revision_id)
2838
testament = _mod_testament.Testament.from_revision(
2841
(status, key, signed_plaintext) = gpg_strategy.verify(signature)
2842
if testament.as_short_text() != signed_plaintext:
2843
return gpg.SIGNATURE_NOT_VALID, None
2844
return (status, key)
1712
2846
def item_keys_introduced_by(self, revision_ids, _files_pb=None):
1713
2847
self._ensure_real()
1714
2848
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()
2849
_files_pb=_files_pb)
1722
2851
def _find_inconsistent_revision_parents(self, revisions_iterator=None):
1723
2852
self._ensure_real()
1742
2870
:param recipe: A search recipe (start, stop, count).
1743
2871
: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))
2873
start_keys = b' '.join(recipe[1])
2874
stop_keys = b' '.join(recipe[2])
2875
count = str(recipe[3]).encode('ascii')
2876
return b'\n'.join((start_keys, stop_keys, count))
1750
2878
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)
2879
parts = search_result.get_network_struct()
2880
return b'\n'.join(parts)
1759
2882
def autopack(self):
1760
path = self.bzrdir._path_for_remote_call(self._client)
2883
path = self.controldir._path_for_remote_call(self._client)
1762
response = self._call('PackRepository.autopack', path)
2885
response = self._call(b'PackRepository.autopack', path)
1763
2886
except errors.UnknownSmartMethod:
1764
2887
self._ensure_real()
1765
2888
self._real_repository._pack_collection.autopack()
1767
2890
self.refresh_data()
1768
if response[0] != 'ok':
1769
raise errors.UnexpectedSmartServerResponse(response)
1772
class RemoteStreamSink(repository.StreamSink):
2891
if response[0] != b'ok':
2892
raise errors.UnexpectedSmartServerResponse(response)
2894
def _revision_archive(self, revision_id, format, name, root, subdir,
2896
path = self.controldir._path_for_remote_call(self._client)
2897
format = format or ''
2899
subdir = subdir or ''
2900
force_mtime = int(force_mtime) if force_mtime is not None else None
2902
response, protocol = self._call_expecting_body(
2903
b'Repository.revision_archive', path,
2905
format.encode('ascii'),
2906
os.path.basename(name).encode('utf-8'),
2907
root.encode('utf-8'),
2908
subdir.encode('utf-8'),
2910
except errors.UnknownSmartMethod:
2912
if response[0] == b'ok':
2913
return iter([protocol.read_body_bytes()])
2914
raise errors.UnexpectedSmartServerResponse(response)
2916
def _annotate_file_revision(self, revid, tree_path, file_id, default_revision):
2917
path = self.controldir._path_for_remote_call(self._client)
2918
tree_path = tree_path.encode('utf-8')
2919
file_id = file_id or b''
2920
default_revision = default_revision or b''
2922
response, handler = self._call_expecting_body(
2923
b'Repository.annotate_file_revision', path,
2924
revid, tree_path, file_id, default_revision)
2925
except errors.UnknownSmartMethod:
2927
if response[0] != b'ok':
2928
raise errors.UnexpectedSmartServerResponse(response)
2929
return map(tuple, bencode.bdecode(handler.read_body_bytes()))
2932
class RemoteStreamSink(vf_repository.StreamSink):
1774
2934
def _insert_real(self, stream, src_format, resume_tokens):
1775
2935
self.target_repo._ensure_real()
2059
3279
def network_name(self):
2060
3280
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)
3282
def open(self, a_controldir, name=None, ignore_fallbacks=False):
3283
return a_controldir.open_branch(name=name,
3284
ignore_fallbacks=ignore_fallbacks)
2066
def _vfs_initialize(self, a_bzrdir, name):
3286
def _vfs_initialize(self, a_controldir, name, append_revisions_only,
2067
3288
# Initialisation when using a local bzrdir object, or a non-vfs init
2068
3289
# method is not available on the server.
2069
3290
# 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,
3292
if isinstance(a_controldir, RemoteBzrDir):
3293
a_controldir._ensure_real()
3294
result = self._custom_format.initialize(a_controldir._real_bzrdir,
3295
name=name, append_revisions_only=append_revisions_only,
3296
repository=repository)
2076
3298
# 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,
3299
result = self._custom_format.initialize(a_controldir, name=name,
3300
append_revisions_only=append_revisions_only,
3301
repository=repository)
3302
if (isinstance(a_controldir, RemoteBzrDir)
3303
and not isinstance(result, RemoteBranch)):
3304
result = RemoteBranch(a_controldir, a_controldir.find_repository(), result,
2084
def initialize(self, a_bzrdir, name=None):
3308
def initialize(self, a_controldir, name=None, repository=None,
3309
append_revisions_only=None):
3311
name = a_controldir._get_selected_branch()
2085
3312
# 1) get the network name to use.
2086
3313
if self._custom_format:
2087
3314
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')()
3316
# Select the current breezy default and ask for that.
3317
reference_bzrdir_format = controldir.format_registry.get(
2091
3319
reference_format = reference_bzrdir_format.get_branch_format()
2092
3320
self._custom_format = reference_format
2093
3321
network_name = reference_format.network_name()
2094
3322
# 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
3323
if not isinstance(a_controldir, RemoteBzrDir):
3324
return self._vfs_initialize(a_controldir, name=name,
3325
append_revisions_only=append_revisions_only,
3326
repository=repository)
3327
medium = a_controldir._client._medium
2098
3328
if medium._is_remote_before((1, 13)):
2099
return self._vfs_initialize(a_bzrdir, name=name)
3329
return self._vfs_initialize(a_controldir, name=name,
3330
append_revisions_only=append_revisions_only,
3331
repository=repository)
2100
3332
# Creating on a remote bzr dir.
2101
3333
# 2) try direct creation via RPC
2102
path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
2103
if name is not None:
3334
path = a_controldir._path_for_remote_call(a_controldir._client)
2104
3336
# XXX JRV20100304: Support creating colocated branches
2105
3337
raise errors.NoColocatedBranchSupport(self)
2106
verb = 'BzrDir.create_branch'
3338
verb = b'BzrDir.create_branch'
2108
response = a_bzrdir._call(verb, path, network_name)
3340
response = a_controldir._call(verb, path, network_name)
2109
3341
except errors.UnknownSmartMethod:
2110
3342
# Fallback - use vfs methods
2111
3343
medium._remember_remote_is_before((1, 13))
2112
return self._vfs_initialize(a_bzrdir, name=name)
2113
if response[0] != 'ok':
3344
return self._vfs_initialize(a_controldir, name=name,
3345
append_revisions_only=append_revisions_only,
3346
repository=repository)
3347
if response[0] != b'ok':
2114
3348
raise errors.UnexpectedSmartServerResponse(response)
2115
3349
# Turn the response into a RemoteRepository object.
2116
3350
format = RemoteBranchFormat(network_name=response[1])
2117
3351
repo_format = response_tuple_to_repo_format(response[3:])
2118
if response[2] == '':
2119
repo_bzrdir = a_bzrdir
3352
repo_path = response[2].decode('utf-8')
3353
if repository is not None:
3354
remote_repo_url = urlutils.join(a_controldir.user_url, repo_path)
3355
url_diff = urlutils.relative_url(repository.user_url,
3358
raise AssertionError(
3359
'repository.user_url %r does not match URL from server '
3360
'response (%r + %r)'
3361
% (repository.user_url, a_controldir.user_url, repo_path))
3362
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)
3365
repo_bzrdir = a_controldir
3367
repo_bzrdir = RemoteBzrDir(
3368
a_controldir.root_transport.clone(
3369
repo_path), a_controldir._format,
3370
a_controldir._client)
3371
remote_repo = RemoteRepository(repo_bzrdir, repo_format)
3372
remote_branch = RemoteBranch(a_controldir, remote_repo,
3373
format=format, setup_stacking=False, name=name)
3374
if append_revisions_only:
3375
remote_branch.set_append_revisions_only(append_revisions_only)
2127
3376
# XXX: We know this is a new branch, so it must have revno 0, revid
2128
3377
# NULL_REVISION. Creating the branch locked would make this be unable
2129
3378
# to be wrong; here its simply very unlikely to be wrong. RBC 20090225
2148
3397
self._ensure_real()
2149
3398
return self._custom_format.supports_set_append_revisions_only()
3400
def stores_revno(self):
3403
def _use_default_local_heads_to_fetch(self):
3404
# If the branch format is a metadir format *and* its heads_to_fetch
3405
# implementation is not overridden vs the base class, we can use the
3406
# base class logic rather than use the heads_to_fetch RPC. This is
3407
# usually cheaper in terms of net round trips, as the last-revision and
3408
# tags info fetched is cached and would be fetched anyway.
3410
if isinstance(self._custom_format, bzrbranch.BranchFormatMetadir):
3411
branch_class = self._custom_format._branch_class()
3412
heads_to_fetch_impl = get_unbound_function(
3413
branch_class.heads_to_fetch)
3414
if heads_to_fetch_impl is get_unbound_function(branch.Branch.heads_to_fetch):
3419
class RemoteBranchStore(_mod_config.IniFileStore):
3420
"""Branch store which attempts to use HPSS calls to retrieve branch store.
3422
Note that this is specific to bzr-based formats.
3425
def __init__(self, branch):
3426
super(RemoteBranchStore, self).__init__()
3427
self.branch = branch
3429
self._real_store = None
3431
def external_url(self):
3432
return urlutils.join(self.branch.user_url, 'branch.conf')
3434
def _load_content(self):
3435
path = self.branch._remote_path()
3437
response, handler = self.branch._call_expecting_body(
3438
b'Branch.get_config_file', path)
3439
except errors.UnknownSmartMethod:
3441
return self._real_store._load_content()
3442
if len(response) and response[0] != b'ok':
3443
raise errors.UnexpectedSmartServerResponse(response)
3444
return handler.read_body_bytes()
3446
def _save_content(self, content):
3447
path = self.branch._remote_path()
3449
response, handler = self.branch._call_with_body_bytes_expecting_body(
3450
b'Branch.put_config_file', (path,
3451
self.branch._lock_token, self.branch._repo_lock_token),
3453
except errors.UnknownSmartMethod:
3455
return self._real_store._save_content(content)
3456
handler.cancel_read_body()
3457
if response != (b'ok', ):
3458
raise errors.UnexpectedSmartServerResponse(response)
3460
def _ensure_real(self):
3461
self.branch._ensure_real()
3462
if self._real_store is None:
3463
self._real_store = _mod_config.BranchStore(self.branch)
2152
3466
class RemoteBranch(branch.Branch, _RpcHelper, lock._RelockDebugMixin):
2153
3467
"""Branch stored on a server accessed by HPSS RPC.
2644
4018
self._ensure_real()
2645
4019
return self._real_branch._set_parent_location(url)
2648
4021
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)
4023
with self.lock_write():
4024
self._clear_cached_state_of_remote_branch_only()
4026
return self._real_branch.pull(
4027
source, overwrite=overwrite, stop_revision=stop_revision,
4028
_override_hook_target=self, **kwargs)
4030
def push(self, target, overwrite=False, stop_revision=None, lossy=False):
4031
with self.lock_read():
4033
return self._real_branch.push(
4034
target, overwrite=overwrite, stop_revision=stop_revision, lossy=lossy,
4035
_override_hook_source_branch=self)
4037
def peek_lock_mode(self):
4038
return self._lock_mode
2663
4040
def is_locked(self):
2664
4041
return self._lock_count >= 1
4043
def revision_id_to_dotted_revno(self, revision_id):
4044
"""Given a revision id, return its dotted revno.
4046
:return: a tuple like (1,) or (400,1,3).
4048
with self.lock_read():
4050
response = self._call(b'Branch.revision_id_to_revno',
4051
self._remote_path(), revision_id)
4052
except errors.UnknownSmartMethod:
4054
return self._real_branch.revision_id_to_dotted_revno(revision_id)
4055
except errors.UnknownErrorFromSmartServer as e:
4056
# Deal with older versions of bzr/brz that didn't explicitly
4057
# wrap GhostRevisionsHaveNoRevno.
4058
if e.error_tuple[1] == b'GhostRevisionsHaveNoRevno':
4059
(revid, ghost_revid) = re.findall(b"{([^}]+)}", e.error_tuple[2])
4060
raise errors.GhostRevisionsHaveNoRevno(
4063
if response[0] == b'ok':
4064
return tuple([int(x) for x in response[1:]])
4066
raise errors.UnexpectedSmartServerResponse(response)
2667
4068
def revision_id_to_revno(self, revision_id):
2669
return self._real_branch.revision_id_to_revno(revision_id)
4069
"""Given a revision id on the branch mainline, return its revno.
4073
with self.lock_read():
4075
response = self._call(b'Branch.revision_id_to_revno',
4076
self._remote_path(), revision_id)
4077
except errors.UnknownSmartMethod:
4079
return self._real_branch.revision_id_to_revno(revision_id)
4080
if response[0] == b'ok':
4081
if len(response) == 2:
4082
return int(response[1])
4083
raise NoSuchRevision(self, revision_id)
4085
raise errors.UnexpectedSmartServerResponse(response)
2672
4087
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)
4088
with self.lock_write():
4089
# XXX: These should be returned by the set_last_revision_info verb
4090
old_revno, old_revid = self.last_revision_info()
4091
self._run_pre_change_branch_tip_hooks(revno, revision_id)
4092
if not revision_id or not isinstance(revision_id, bytes):
4093
raise errors.InvalidRevisionId(
4094
revision_id=revision_id, branch=self)
4096
response = self._call(b'Branch.set_last_revision_info',
4097
self._remote_path(), self._lock_token, self._repo_lock_token,
4098
str(revno).encode('ascii'), revision_id)
4099
except errors.UnknownSmartMethod:
4101
self._clear_cached_state_of_remote_branch_only()
4102
self._real_branch.set_last_revision_info(revno, revision_id)
4103
self._last_revision_info_cache = revno, revision_id
4105
if response == (b'ok',):
4106
self._clear_cached_state()
4107
self._last_revision_info_cache = revno, revision_id
4108
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
4109
# Update the _real_branch's cache too.
4110
if self._real_branch is not None:
4111
cache = self._last_revision_info_cache
4112
self._real_branch._last_revision_info_cache = cache
4114
raise errors.UnexpectedSmartServerResponse(response)
2699
4116
def generate_revision_history(self, revision_id, last_rev=None,
2700
4117
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))
4118
with self.lock_write():
4119
medium = self._client._medium
4120
if not medium._is_remote_before((1, 6)):
4121
# Use a smart method for 1.6 and above servers
4123
self._set_last_revision_descendant(revision_id, other_branch,
4124
allow_diverged=True, allow_overwrite_descendant=True)
4126
except errors.UnknownSmartMethod:
4127
medium._remember_remote_is_before((1, 6))
4128
self._clear_cached_state_of_remote_branch_only()
4129
graph = self.repository.get_graph()
4130
(last_revno, last_revid) = self.last_revision_info()
4131
known_revision_ids = [
4132
(last_revid, last_revno),
4133
(_mod_revision.NULL_REVISION, 0),
4135
if last_rev is not None:
4136
if not graph.is_ancestor(last_rev, revision_id):
4137
# our previous tip is not merged into stop_revision
4138
raise errors.DivergedBranches(self, other_branch)
4139
revno = graph.find_distance_to_null(
4140
revision_id, known_revision_ids)
4141
self.set_last_revision_info(revno, revision_id)
2714
4143
def set_push_location(self, location):
4144
self._set_config_location('push_location', location)
4146
def heads_to_fetch(self):
4147
if self._format._use_default_local_heads_to_fetch():
4148
# We recognise this format, and its heads-to-fetch implementation
4149
# is the default one (tip + tags). In this case it's cheaper to
4150
# just use the default implementation rather than a special RPC as
4151
# the tip and tags data is cached.
4152
return branch.Branch.heads_to_fetch(self)
4153
medium = self._client._medium
4154
if medium._is_remote_before((2, 4)):
4155
return self._vfs_heads_to_fetch()
4157
return self._rpc_heads_to_fetch()
4158
except errors.UnknownSmartMethod:
4159
medium._remember_remote_is_before((2, 4))
4160
return self._vfs_heads_to_fetch()
4162
def _rpc_heads_to_fetch(self):
4163
response = self._call(b'Branch.heads_to_fetch', self._remote_path())
4164
if len(response) != 2:
4165
raise errors.UnexpectedSmartServerResponse(response)
4166
must_fetch, if_present_fetch = response
4167
return set(must_fetch), set(if_present_fetch)
4169
def _vfs_heads_to_fetch(self):
2715
4170
self._ensure_real()
2716
return self._real_branch.set_push_location(location)
4171
return self._real_branch.heads_to_fetch()
4173
def reconcile(self, thorough=True):
4174
"""Make sure the data stored in this branch is consistent."""
4175
from .reconcile import BranchReconciler
4176
with self.lock_write():
4177
reconciler = BranchReconciler(self, thorough=thorough)
4178
return reconciler.reconcile()
2719
4181
class RemoteConfig(object):
2774
4246
medium = self._branch._client._medium
2775
4247
if medium._is_remote_before((1, 14)):
2776
4248
return self._vfs_set_option(value, name, section)
2778
path = self._branch._remote_path()
2779
response = self._branch._client.call('Branch.set_config_option',
4249
if isinstance(value, dict):
4250
if medium._is_remote_before((2, 2)):
4251
return self._vfs_set_option(value, name, section)
4252
return self._set_config_option_dict(value, name, section)
4254
return self._set_config_option(value, name, section)
4256
def _set_config_option(self, value, name, section):
4257
if isinstance(value, (bool, int)):
4259
elif isinstance(value, (text_type, str)):
4262
raise TypeError(value)
4264
path = self._branch._remote_path()
4265
response = self._branch._client.call(b'Branch.set_config_option',
4266
path, self._branch._lock_token, self._branch._repo_lock_token,
4267
value.encode('utf-8'), name.encode('utf-8'),
4268
(section or '').encode('utf-8'))
4269
except errors.UnknownSmartMethod:
4270
medium = self._branch._client._medium
4271
medium._remember_remote_is_before((1, 14))
4272
return self._vfs_set_option(value, name, section)
4274
raise errors.UnexpectedSmartServerResponse(response)
4276
def _serialize_option_dict(self, option_dict):
4278
for key, value in option_dict.items():
4279
if isinstance(key, text_type):
4280
key = key.encode('utf8')
4281
if isinstance(value, text_type):
4282
value = value.encode('utf8')
4283
utf8_dict[key] = value
4284
return bencode.bencode(utf8_dict)
4286
def _set_config_option_dict(self, value, name, section):
4288
path = self._branch._remote_path()
4289
serialised_dict = self._serialize_option_dict(value)
4290
response = self._branch._client.call(
4291
b'Branch.set_config_option_dict',
2780
4292
path, self._branch._lock_token, self._branch._repo_lock_token,
2781
value.encode('utf8'), name, section or '')
4293
serialised_dict, name.encode('utf-8'), (section or '').encode('utf-8'))
2782
4294
except errors.UnknownSmartMethod:
2783
medium._remember_remote_is_before((1, 14))
4295
medium = self._branch._client._medium
4296
medium._remember_remote_is_before((2, 2))
2784
4297
return self._vfs_set_option(value, name, section)
2785
4298
if response != ():
2786
4299
raise errors.UnexpectedSmartServerResponse(response)
2856
4363
def find(name):
2858
4365
return context[name]
2859
except KeyError, key_err:
2860
mutter('Missing key %r in context %r', key_err.args[0], context)
4367
mutter('Missing key \'%s\' in context %r', name, context)
2862
4370
def get_path():
2863
4371
"""Get the path from the context if present, otherwise use first error
2867
4375
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)
4378
return err.error_args[0].decode('utf-8')
4380
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)
4382
if not isinstance(err.error_verb, bytes):
4383
raise TypeError(err.error_verb)
4385
translator = error_translators.get(err.error_verb)
4389
raise translator(err, find, get_path)
4391
translator = no_context_error_translators.get(err.error_verb)
4393
raise errors.UnknownErrorFromSmartServer(err)
4395
raise translator(err)
4398
error_translators.register(b'NoSuchRevision',
4399
lambda err, find, get_path: NoSuchRevision(
4400
find('branch'), err.error_args[0]))
4401
error_translators.register(b'nosuchrevision',
4402
lambda err, find, get_path: NoSuchRevision(
4403
find('repository'), err.error_args[0]))
4404
error_translators.register(
4405
b'revno-outofbounds',
4406
lambda err, find, get_path: errors.RevnoOutOfBounds(
4407
err.error_args[0], (err.error_args[1], err.error_args[2])))
4410
def _translate_nobranch_error(err, find, get_path):
4411
if len(err.error_args) >= 1:
4412
extra = err.error_args[0].decode('utf-8')
4415
return errors.NotBranchError(path=find('bzrdir').root_transport.base,
4419
error_translators.register(b'nobranch', _translate_nobranch_error)
4420
error_translators.register(b'norepository',
4421
lambda err, find, get_path: errors.NoRepositoryPresent(
4423
error_translators.register(b'UnlockableTransport',
4424
lambda err, find, get_path: errors.UnlockableTransport(
4425
find('bzrdir').root_transport))
4426
error_translators.register(b'TokenMismatch',
4427
lambda err, find, get_path: errors.TokenMismatch(
4428
find('token'), '(remote token)'))
4429
error_translators.register(b'Diverged',
4430
lambda err, find, get_path: errors.DivergedBranches(
4431
find('branch'), find('other_branch')))
4432
error_translators.register(b'NotStacked',
4433
lambda err, find, get_path: errors.NotStacked(branch=find('branch')))
4436
def _translate_PermissionDenied(err, find, get_path):
4438
if len(err.error_args) >= 2:
4439
extra = err.error_args[1].decode('utf-8')
4442
return errors.PermissionDenied(path, extra=extra)
4445
error_translators.register(b'PermissionDenied', _translate_PermissionDenied)
4446
error_translators.register(b'ReadError',
4447
lambda err, find, get_path: errors.ReadError(get_path()))
4448
error_translators.register(b'NoSuchFile',
4449
lambda err, find, get_path: errors.NoSuchFile(get_path()))
4450
error_translators.register(b'TokenLockingNotSupported',
4451
lambda err, find, get_path: errors.TokenLockingNotSupported(
4452
find('repository')))
4453
error_translators.register(b'UnsuspendableWriteGroup',
4454
lambda err, find, get_path: errors.UnsuspendableWriteGroup(
4455
repository=find('repository')))
4456
error_translators.register(b'UnresumableWriteGroup',
4457
lambda err, find, get_path: errors.UnresumableWriteGroup(
4458
repository=find('repository'), write_groups=err.error_args[0],
4459
reason=err.error_args[1]))
4460
no_context_error_translators.register(b'GhostRevisionsHaveNoRevno',
4461
lambda err: errors.GhostRevisionsHaveNoRevno(*err.error_args))
4462
no_context_error_translators.register(b'IncompatibleRepositories',
4463
lambda err: errors.IncompatibleRepositories(
4464
err.error_args[0].decode('utf-8'), err.error_args[1].decode('utf-8'), err.error_args[2].decode('utf-8')))
4465
no_context_error_translators.register(b'LockContention',
4466
lambda err: errors.LockContention('(remote lock)'))
4467
no_context_error_translators.register(b'LockFailed',
4468
lambda err: errors.LockFailed(err.error_args[0].decode('utf-8'), err.error_args[1].decode('utf-8')))
4469
no_context_error_translators.register(b'TipChangeRejected',
4470
lambda err: errors.TipChangeRejected(err.error_args[0].decode('utf8')))
4471
no_context_error_translators.register(b'UnstackableBranchFormat',
4472
lambda err: branch.UnstackableBranchFormat(*err.error_args))
4473
no_context_error_translators.register(b'UnstackableRepositoryFormat',
4474
lambda err: errors.UnstackableRepositoryFormat(*err.error_args))
4475
no_context_error_translators.register(b'FileExists',
4476
lambda err: errors.FileExists(err.error_args[0].decode('utf-8')))
4477
no_context_error_translators.register(b'DirectoryNotEmpty',
4478
lambda err: errors.DirectoryNotEmpty(err.error_args[0].decode('utf-8')))
4479
no_context_error_translators.register(b'UnknownFormat',
4480
lambda err: errors.UnknownFormatError(
4481
err.error_args[0].decode('ascii'), err.error_args[0].decode('ascii')))
4482
no_context_error_translators.register(b'InvalidURL',
4483
lambda err: urlutils.InvalidURL(
4484
err.error_args[0].decode('utf-8'), err.error_args[1].decode('ascii')))
4487
def _translate_short_readv_error(err):
4488
args = err.error_args
4489
return errors.ShortReadvError(
4490
args[0].decode('utf-8'),
4491
int(args[1].decode('ascii')), int(args[2].decode('ascii')),
4492
int(args[3].decode('ascii')))
4495
no_context_error_translators.register(b'ShortReadvError',
4496
_translate_short_readv_error)
4499
def _translate_unicode_error(err):
4500
encoding = err.error_args[0].decode('ascii')
4501
val = err.error_args[1].decode('utf-8')
4502
start = int(err.error_args[2].decode('ascii'))
4503
end = int(err.error_args[3].decode('ascii'))
4504
reason = err.error_args[4].decode('utf-8')
4505
if val.startswith('u:'):
4506
val = val[2:].decode('utf-8')
4507
elif val.startswith('s:'):
4508
val = val[2:].decode('base64')
4509
if err.error_verb == 'UnicodeDecodeError':
4510
raise UnicodeDecodeError(encoding, val, start, end, reason)
4511
elif err.error_verb == 'UnicodeEncodeError':
4512
raise UnicodeEncodeError(encoding, val, start, end, reason)
4515
no_context_error_translators.register(b'UnicodeEncodeError',
4516
_translate_unicode_error)
4517
no_context_error_translators.register(b'UnicodeDecodeError',
4518
_translate_unicode_error)
4519
no_context_error_translators.register(b'ReadOnlyError',
4520
lambda err: errors.TransportNotPossible('readonly transport'))
4521
no_context_error_translators.register(b'MemoryError',
4522
lambda err: errors.BzrError("remote server out of memory\n"
4523
"Retry non-remotely, or contact the server admin for details."))
4524
no_context_error_translators.register(b'RevisionNotPresent',
4525
lambda err: errors.RevisionNotPresent(err.error_args[0].decode('utf-8'), err.error_args[1].decode('utf-8')))
4527
no_context_error_translators.register(b'BzrCheckError',
4528
lambda err: errors.BzrCheckError(msg=err.error_args[0].decode('utf-8')))