73
104
return self._client.call_with_body_bytes_expecting_body(
74
105
method, args, body_bytes)
75
except errors.ErrorFromSmartServer, err:
106
except errors.ErrorFromSmartServer as err:
76
107
self._translate_error(err, **err_context)
79
110
def response_tuple_to_repo_format(response):
80
111
"""Convert a response tuple describing a repository format to a format."""
81
112
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')
113
format._rich_root_data = (response[0] == b'yes')
114
format._supports_tree_reference = (response[1] == b'yes')
115
format._supports_external_lookups = (response[2] == b'yes')
85
116
format._network_name = response[3]
89
# Note: RemoteBzrDirFormat is in bzrdir.py
91
class RemoteBzrDir(BzrDir, _RpcHelper):
120
# Note that RemoteBzrDirProber lives in breezy.bzrdir so breezy.bzr.remote
121
# does not have to be imported unless a remote format is involved.
123
class RemoteBzrDirFormat(_mod_bzrdir.BzrDirMetaFormat1):
124
"""Format representing bzrdirs accessed via a smart server"""
126
supports_workingtrees = False
128
colocated_branches = False
131
_mod_bzrdir.BzrDirMetaFormat1.__init__(self)
132
# XXX: It's a bit ugly that the network name is here, because we'd
133
# like to believe that format objects are stateless or at least
134
# immutable, However, we do at least avoid mutating the name after
135
# it's returned. See <https://bugs.launchpad.net/bzr/+bug/504102>
136
self._network_name = None
139
return "%s(_network_name=%r)" % (self.__class__.__name__,
142
def get_format_description(self):
143
if self._network_name:
145
real_format = controldir.network_format_registry.get(
150
return 'Remote: ' + real_format.get_format_description()
151
return 'bzr remote bzrdir'
153
def get_format_string(self):
154
raise NotImplementedError(self.get_format_string)
156
def network_name(self):
157
if self._network_name:
158
return self._network_name
160
raise AssertionError("No network name set.")
162
def initialize_on_transport(self, transport):
164
# hand off the request to the smart server
165
client_medium = transport.get_smart_medium()
166
except errors.NoSmartMedium:
167
# TODO: lookup the local format from a server hint.
168
local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
169
return local_dir_format.initialize_on_transport(transport)
170
client = _SmartClient(client_medium)
171
path = client.remote_path_from_transport(transport)
173
response = client.call(b'BzrDirFormat.initialize', path)
174
except errors.ErrorFromSmartServer as err:
175
_translate_error(err, path=path)
176
if response[0] != b'ok':
177
raise errors.SmartProtocolError(
178
'unexpected response code %s' % (response,))
179
format = RemoteBzrDirFormat()
180
self._supply_sub_formats_to(format)
181
return RemoteBzrDir(transport, format)
183
def parse_NoneTrueFalse(self, arg):
190
raise AssertionError("invalid arg %r" % arg)
192
def _serialize_NoneTrueFalse(self, arg):
199
def _serialize_NoneString(self, arg):
202
def initialize_on_transport_ex(self, transport, use_existing_dir=False,
203
create_prefix=False, force_new_repo=False, stacked_on=None,
204
stack_on_pwd=None, repo_format_name=None, make_working_trees=None,
207
# hand off the request to the smart server
208
client_medium = transport.get_smart_medium()
209
except errors.NoSmartMedium:
212
# Decline to open it if the server doesn't support our required
213
# version (3) so that the VFS-based transport will do it.
214
if client_medium.should_probe():
216
server_version = client_medium.protocol_version()
217
if server_version != '2':
221
except errors.SmartProtocolError:
222
# Apparently there's no usable smart server there, even though
223
# the medium supports the smart protocol.
228
client = _SmartClient(client_medium)
229
path = client.remote_path_from_transport(transport)
230
if client_medium._is_remote_before((1, 16)):
233
# TODO: lookup the local format from a server hint.
234
local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
235
self._supply_sub_formats_to(local_dir_format)
236
return local_dir_format.initialize_on_transport_ex(transport,
237
use_existing_dir=use_existing_dir, create_prefix=create_prefix,
238
force_new_repo=force_new_repo, stacked_on=stacked_on,
239
stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
240
make_working_trees=make_working_trees, shared_repo=shared_repo,
242
return self._initialize_on_transport_ex_rpc(client, path, transport,
243
use_existing_dir, create_prefix, force_new_repo, stacked_on,
244
stack_on_pwd, repo_format_name, make_working_trees, shared_repo)
246
def _initialize_on_transport_ex_rpc(self, client, path, transport,
247
use_existing_dir, create_prefix, force_new_repo, stacked_on,
248
stack_on_pwd, repo_format_name, make_working_trees, shared_repo):
250
args.append(self._serialize_NoneTrueFalse(use_existing_dir))
251
args.append(self._serialize_NoneTrueFalse(create_prefix))
252
args.append(self._serialize_NoneTrueFalse(force_new_repo))
253
args.append(self._serialize_NoneString(stacked_on))
254
# stack_on_pwd is often/usually our transport
257
stack_on_pwd = transport.relpath(stack_on_pwd).encode('utf-8')
260
except errors.PathNotChild:
262
args.append(self._serialize_NoneString(stack_on_pwd))
263
args.append(self._serialize_NoneString(repo_format_name))
264
args.append(self._serialize_NoneTrueFalse(make_working_trees))
265
args.append(self._serialize_NoneTrueFalse(shared_repo))
266
request_network_name = self._network_name or \
267
_mod_bzrdir.BzrDirFormat.get_default_format().network_name()
269
response = client.call(b'BzrDirFormat.initialize_ex_1.16',
270
request_network_name, path, *args)
271
except errors.UnknownSmartMethod:
272
client._medium._remember_remote_is_before((1, 16))
273
local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
274
self._supply_sub_formats_to(local_dir_format)
275
return local_dir_format.initialize_on_transport_ex(transport,
276
use_existing_dir=use_existing_dir, create_prefix=create_prefix,
277
force_new_repo=force_new_repo, stacked_on=stacked_on,
278
stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
279
make_working_trees=make_working_trees, shared_repo=shared_repo,
281
except errors.ErrorFromSmartServer as err:
282
_translate_error(err, path=path.decode('utf-8'))
283
repo_path = response[0]
284
bzrdir_name = response[6]
285
require_stacking = response[7]
286
require_stacking = self.parse_NoneTrueFalse(require_stacking)
287
format = RemoteBzrDirFormat()
288
format._network_name = bzrdir_name
289
self._supply_sub_formats_to(format)
290
bzrdir = RemoteBzrDir(transport, format, _client=client)
292
repo_format = response_tuple_to_repo_format(response[1:])
293
if repo_path == b'.':
295
repo_path = repo_path.decode('utf-8')
297
repo_bzrdir_format = RemoteBzrDirFormat()
298
repo_bzrdir_format._network_name = response[5]
299
repo_bzr = RemoteBzrDir(transport.clone(repo_path),
303
final_stack = response[8] or None
305
final_stack = final_stack.decode('utf-8')
306
final_stack_pwd = response[9] or None
308
final_stack_pwd = urlutils.join(
309
transport.base, final_stack_pwd.decode('utf-8'))
310
remote_repo = RemoteRepository(repo_bzr, repo_format)
311
if len(response) > 10:
312
# Updated server verb that locks remotely.
313
repo_lock_token = response[10] or None
314
remote_repo.lock_write(repo_lock_token, _skip_rpc=True)
316
remote_repo.dont_leave_lock_in_place()
318
remote_repo.lock_write()
319
policy = _mod_bzrdir.UseExistingRepository(remote_repo,
320
final_stack, final_stack_pwd, require_stacking)
321
policy.acquire_repository()
325
bzrdir._format.set_branch_format(self.get_branch_format())
327
# The repo has already been created, but we need to make sure that
328
# we'll make a stackable branch.
329
bzrdir._format.require_stacking(_skip_repo=True)
330
return remote_repo, bzrdir, require_stacking, policy
332
def _open(self, transport):
333
return RemoteBzrDir(transport, self)
335
def __eq__(self, other):
336
if not isinstance(other, RemoteBzrDirFormat):
338
return self.get_format_description() == other.get_format_description()
340
def __return_repository_format(self):
341
# Always return a RemoteRepositoryFormat object, but if a specific bzr
342
# repository format has been asked for, tell the RemoteRepositoryFormat
343
# that it should use that for init() etc.
344
result = RemoteRepositoryFormat()
345
custom_format = getattr(self, '_repository_format', None)
347
if isinstance(custom_format, RemoteRepositoryFormat):
350
# We will use the custom format to create repositories over the
351
# wire; expose its details like rich_root_data for code to
353
result._custom_format = custom_format
356
def get_branch_format(self):
357
result = _mod_bzrdir.BzrDirMetaFormat1.get_branch_format(self)
358
if not isinstance(result, RemoteBranchFormat):
359
new_result = RemoteBranchFormat()
360
new_result._custom_format = result
362
self.set_branch_format(new_result)
366
repository_format = property(__return_repository_format,
367
_mod_bzrdir.BzrDirMetaFormat1._set_repository_format) # .im_func)
370
class RemoteControlStore(_mod_config.IniFileStore):
371
"""Control store which attempts to use HPSS calls to retrieve control store.
373
Note that this is specific to bzr-based formats.
376
def __init__(self, bzrdir):
377
super(RemoteControlStore, self).__init__()
378
self.controldir = bzrdir
379
self._real_store = None
381
def lock_write(self, token=None):
383
return self._real_store.lock_write(token)
387
return self._real_store.unlock()
390
with self.lock_write():
391
# We need to be able to override the undecorated implementation
392
self.save_without_locking()
394
def save_without_locking(self):
395
super(RemoteControlStore, self).save()
397
def _ensure_real(self):
398
self.controldir._ensure_real()
399
if self._real_store is None:
400
self._real_store = _mod_config.ControlStore(self.controldir)
402
def external_url(self):
403
return urlutils.join(self.branch.user_url, 'control.conf')
405
def _load_content(self):
406
medium = self.controldir._client._medium
407
path = self.controldir._path_for_remote_call(self.controldir._client)
409
response, handler = self.controldir._call_expecting_body(
410
b'BzrDir.get_config_file', path)
411
except errors.UnknownSmartMethod:
413
return self._real_store._load_content()
414
if len(response) and response[0] != b'ok':
415
raise errors.UnexpectedSmartServerResponse(response)
416
return handler.read_body_bytes()
418
def _save_content(self, content):
419
# FIXME JRV 2011-11-22: Ideally this should use a
420
# HPSS call too, but at the moment it is not possible
421
# to write lock control directories.
423
return self._real_store._save_content(content)
426
class RemoteBzrDir(_mod_bzrdir.BzrDir, _RpcHelper):
92
427
"""Control directory on a remote server, accessed via bzr:// or similar."""
94
429
def __init__(self, transport, format, _client=None, _force_probe=False):
186
563
medium = self._client._medium
187
564
if medium._is_remote_before((1, 13)):
188
565
return self._vfs_cloning_metadir(require_stacking=require_stacking)
189
verb = 'BzrDir.cloning_metadir'
566
verb = b'BzrDir.cloning_metadir'
190
567
if require_stacking:
194
571
path = self._path_for_remote_call(self._client)
196
573
response = self._call(verb, path, stacking)
197
574
except errors.UnknownSmartMethod:
198
575
medium._remember_remote_is_before((1, 13))
199
576
return self._vfs_cloning_metadir(require_stacking=require_stacking)
200
except errors.UnknownErrorFromSmartServer, err:
201
if err.error_tuple != ('BranchReference',):
577
except errors.UnknownErrorFromSmartServer as err:
578
if err.error_tuple != (b'BranchReference',):
203
580
# We need to resolve the branch reference to determine the
204
581
# cloning_metadir. This causes unnecessary RPCs to open the
205
582
# referenced branch (and bzrdir, etc) but only when the caller
206
583
# didn't already resolve the branch reference.
207
584
referenced_branch = self.open_branch()
208
return referenced_branch.bzrdir.cloning_metadir()
585
return referenced_branch.controldir.cloning_metadir()
209
586
if len(response) != 3:
210
587
raise errors.UnexpectedSmartServerResponse(response)
211
588
control_name, repo_name, branch_info = response
212
589
if len(branch_info) != 2:
213
590
raise errors.UnexpectedSmartServerResponse(response)
214
591
branch_ref, branch_name = branch_info
215
format = bzrdir.network_format_registry.get(control_name)
593
format = controldir.network_format_registry.get(control_name)
595
raise errors.UnknownFormatError(
596
kind='control', format=control_name)
217
format.repository_format = repository.network_format_registry.get(
219
if branch_ref == 'ref':
600
format.repository_format = _mod_repository.network_format_registry.get(
603
raise errors.UnknownFormatError(kind='repository',
605
if branch_ref == b'ref':
220
606
# XXX: we need possible_transports here to avoid reopening the
221
607
# connection to the referenced location
222
ref_bzrdir = BzrDir.open(branch_name)
608
ref_bzrdir = _mod_bzrdir.BzrDir.open(branch_name)
223
609
branch_format = ref_bzrdir.cloning_metadir().get_branch_format()
224
610
format.set_branch_format(branch_format)
225
elif branch_ref == 'branch':
611
elif branch_ref == b'branch':
227
format.set_branch_format(
228
branch.network_format_registry.get(branch_name))
614
branch_format = branch.network_format_registry.get(
617
raise errors.UnknownFormatError(kind='branch',
619
format.set_branch_format(branch_format)
230
621
raise errors.UnexpectedSmartServerResponse(response)
266
674
def destroy_branch(self, name=None):
267
675
"""See BzrDir.destroy_branch"""
269
self._real_bzrdir.destroy_branch(name=name)
677
name = self._get_selected_branch()
679
raise errors.NoColocatedBranchSupport(self)
680
path = self._path_for_remote_call(self._client)
686
response = self._call(b'BzrDir.destroy_branch', path, *args)
687
except errors.UnknownSmartMethod:
689
self._real_bzrdir.destroy_branch(name=name)
690
self._next_open_branch_result = None
270
692
self._next_open_branch_result = None
693
if response[0] != b'ok':
694
raise SmartProtocolError(
695
'unexpected response code %s' % (response,))
272
def create_workingtree(self, revision_id=None, from_branch=None):
697
def create_workingtree(self, revision_id=None, from_branch=None,
698
accelerator_tree=None, hardlink=False):
273
699
raise errors.NotLocalUrl(self.transport.base)
275
def find_branch_format(self):
701
def find_branch_format(self, name=None):
276
702
"""Find the branch 'format' for this bzrdir.
278
704
This might be a synthetic object for e.g. RemoteBranch and SVN.
280
b = self.open_branch()
706
b = self.open_branch(name=name)
283
def get_branch_reference(self):
709
def get_branches(self, possible_transports=None, ignore_fallbacks=False):
710
path = self._path_for_remote_call(self._client)
712
response, handler = self._call_expecting_body(
713
b'BzrDir.get_branches', path)
714
except errors.UnknownSmartMethod:
716
return self._real_bzrdir.get_branches()
717
if response[0] != b"success":
718
raise errors.UnexpectedSmartServerResponse(response)
719
body = bencode.bdecode(handler.read_body_bytes())
721
for name, value in viewitems(body):
722
name = name.decode('utf-8')
723
ret[name] = self._open_branch(name, value[0], value[1],
724
possible_transports=possible_transports,
725
ignore_fallbacks=ignore_fallbacks)
728
def set_branch_reference(self, target_branch, name=None):
729
"""See BzrDir.set_branch_reference()."""
731
name = self._get_selected_branch()
733
raise errors.NoColocatedBranchSupport(self)
735
return self._real_bzrdir.set_branch_reference(target_branch, name=name)
737
def get_branch_reference(self, name=None):
284
738
"""See BzrDir.get_branch_reference()."""
740
name = self._get_selected_branch()
742
raise errors.NoColocatedBranchSupport(self)
285
743
response = self._get_branch_reference()
286
744
if response[0] == 'ref':
745
return response[1].decode('utf-8')
291
749
def _get_branch_reference(self):
750
"""Get branch reference information
752
:return: Tuple with (kind, location_or_format)
753
if kind == 'ref', then location_or_format contains a location
754
otherwise, it contains a format name
292
756
path = self._path_for_remote_call(self._client)
293
757
medium = self._client._medium
294
758
candidate_calls = [
295
('BzrDir.open_branchV3', (2, 1)),
296
('BzrDir.open_branchV2', (1, 13)),
297
('BzrDir.open_branch', None),
759
(b'BzrDir.open_branchV3', (2, 1)),
760
(b'BzrDir.open_branchV2', (1, 13)),
761
(b'BzrDir.open_branch', None),
299
763
for verb, required_version in candidate_calls:
300
764
if required_version and medium._is_remote_before(required_version):
307
771
medium._remember_remote_is_before(required_version)
310
if verb == 'BzrDir.open_branch':
311
if response[0] != 'ok':
774
if verb == b'BzrDir.open_branch':
775
if response[0] != b'ok':
312
776
raise errors.UnexpectedSmartServerResponse(response)
313
if response[1] != '':
777
if response[1] != b'':
314
778
return ('ref', response[1])
316
return ('branch', '')
317
if response[0] not in ('ref', 'branch'):
780
return ('branch', b'')
781
if response[0] not in (b'ref', b'branch'):
318
782
raise errors.UnexpectedSmartServerResponse(response)
783
return (response[0].decode('ascii'), response[1])
321
def _get_tree_branch(self):
785
def _get_tree_branch(self, name=None):
322
786
"""See BzrDir._get_tree_branch()."""
323
return None, self.open_branch()
787
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':
789
def _open_branch(self, name, kind, location_or_format,
790
ignore_fallbacks=False, possible_transports=None):
336
792
# a branch reference, use the existing BranchReference logic.
337
793
format = BranchReferenceFormat()
338
794
return format.open(self, name=name, _found=True,
339
location=response[1], ignore_fallbacks=ignore_fallbacks)
340
branch_format_name = response[1]
795
location=location_or_format.decode('utf-8'),
796
ignore_fallbacks=ignore_fallbacks,
797
possible_transports=possible_transports)
798
branch_format_name = location_or_format
341
799
if not branch_format_name:
342
800
branch_format_name = None
343
801
format = RemoteBranchFormat(network_name=branch_format_name)
344
802
return RemoteBranch(self, self.find_repository(), format=format,
345
setup_stacking=not ignore_fallbacks, name=name)
803
setup_stacking=not ignore_fallbacks, name=name,
804
possible_transports=possible_transports)
806
def open_branch(self, name=None, unsupported=False,
807
ignore_fallbacks=False, possible_transports=None):
809
name = self._get_selected_branch()
811
raise errors.NoColocatedBranchSupport(self)
813
raise NotImplementedError(
814
'unsupported flag support not implemented yet.')
815
if self._next_open_branch_result is not None:
816
# See create_branch for details.
817
result = self._next_open_branch_result
818
self._next_open_branch_result = None
820
response = self._get_branch_reference()
821
return self._open_branch(name, response[0], response[1],
822
possible_transports=possible_transports,
823
ignore_fallbacks=ignore_fallbacks)
347
825
def _open_repo_v1(self, path):
348
verb = 'BzrDir.find_repository'
826
verb = b'BzrDir.find_repository'
349
827
response = self._call(verb, path)
350
if response[0] != 'ok':
828
if response[0] != b'ok':
351
829
raise errors.UnexpectedSmartServerResponse(response)
352
830
# servers that only support the v1 method don't support external
353
831
# references either.
354
832
self._ensure_real()
355
833
repo = self._real_bzrdir.open_repository()
356
response = response + ('no', repo._format.network_name())
834
response = response + (b'no', repo._format.network_name())
357
835
return response, repo
359
837
def _open_repo_v2(self, path):
360
verb = 'BzrDir.find_repositoryV2'
838
verb = b'BzrDir.find_repositoryV2'
361
839
response = self._call(verb, path)
362
if response[0] != 'ok':
840
if response[0] != b'ok':
363
841
raise errors.UnexpectedSmartServerResponse(response)
364
842
self._ensure_real()
365
843
repo = self._real_bzrdir.open_repository()
441
936
"""Upgrading of remote bzrdirs is not supported yet."""
444
def needs_format_conversion(self, format=None):
939
def needs_format_conversion(self, format):
445
940
"""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
943
def _get_config(self):
458
944
return RemoteBzrDirConfig(self)
461
class RemoteRepositoryFormat(repository.RepositoryFormat):
946
def _get_config_store(self):
947
return RemoteControlStore(self)
950
class RemoteInventoryTree(InventoryRevisionTree):
952
def __init__(self, repository, inv, revision_id):
953
super(RemoteInventoryTree, self).__init__(repository, inv, revision_id)
955
def archive(self, format, name, root=None, subdir=None, force_mtime=None):
956
ret = self._repository._revision_archive(
957
self.get_revision_id(), format, name, root, subdir,
958
force_mtime=force_mtime)
960
return super(RemoteInventoryTree, self).archive(
961
format, name, root, subdir, force_mtime=force_mtime)
964
def annotate_iter(self, path, file_id=None,
965
default_revision=_mod_revision.CURRENT_REVISION):
966
"""Return an iterator of revision_id, line tuples.
968
For working trees (and mutable trees in general), the special
969
revision_id 'current:' will be used for lines that are new in this
970
tree, e.g. uncommitted changes.
971
:param file_id: The file to produce an annotated version from
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, default_revision)
979
return super(RemoteInventoryTree, self).annotate_iter(
980
path, file_id, default_revision=default_revision)
984
class RemoteRepositoryFormat(vf_repository.VersionedFileRepositoryFormat):
462
985
"""Format for repositories accessed over a _SmartClient.
464
987
Instances of this repository are represented by RemoteRepository
529
1074
self._custom_format.supports_tree_reference
530
1075
return self._supports_tree_reference
532
def _vfs_initialize(self, a_bzrdir, shared):
1078
def revision_graph_can_have_wrong_parents(self):
1079
if self._revision_graph_can_have_wrong_parents is None:
1081
self._revision_graph_can_have_wrong_parents = \
1082
self._custom_format.revision_graph_can_have_wrong_parents
1083
return self._revision_graph_can_have_wrong_parents
1085
def _vfs_initialize(self, a_controldir, shared):
533
1086
"""Helper for common code in initialize."""
534
1087
if self._custom_format:
535
1088
# Custom format requested
536
result = self._custom_format.initialize(a_bzrdir, shared=shared)
1089
result = self._custom_format.initialize(
1090
a_controldir, shared=shared)
537
1091
elif self._creating_bzrdir is not None:
538
1092
# Use the format that the repository we were created to back
540
1094
prior_repo = self._creating_bzrdir.open_repository()
541
1095
prior_repo._ensure_real()
542
1096
result = prior_repo._real_repository._format.initialize(
543
a_bzrdir, shared=shared)
1097
a_controldir, shared=shared)
545
1099
# assume that a_bzr is a RemoteBzrDir but the smart server didn't
546
1100
# support remote initialization.
547
1101
# We delegate to a real object at this point (as RemoteBzrDir
548
1102
# 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)
1103
# recursion if we just called a_controldir.create_repository.
1104
a_controldir._ensure_real()
1105
result = a_controldir._real_bzrdir.create_repository(shared=shared)
552
1106
if not isinstance(result, RemoteRepository):
553
return self.open(a_bzrdir)
1107
return self.open(a_controldir)
557
def initialize(self, a_bzrdir, shared=False):
1111
def initialize(self, a_controldir, shared=False):
558
1112
# 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
1113
if not isinstance(a_controldir, RemoteBzrDir):
1114
return self._vfs_initialize(a_controldir, shared)
1115
medium = a_controldir._client._medium
562
1116
if medium._is_remote_before((1, 13)):
563
return self._vfs_initialize(a_bzrdir, shared)
1117
return self._vfs_initialize(a_controldir, shared)
564
1118
# Creating on a remote bzr dir.
565
1119
# 1) get the network name to use.
566
1120
if self._custom_format:
568
1122
elif self._network_name:
569
1123
network_name = self._network_name
571
# Select the current bzrlib default and ask for that.
572
reference_bzrdir_format = bzrdir.format_registry.get('default')()
1125
# Select the current breezy default and ask for that.
1126
reference_bzrdir_format = controldir.format_registry.get(
573
1128
reference_format = reference_bzrdir_format.repository_format
574
1129
network_name = reference_format.network_name()
575
1130
# 2) try direct creation via RPC
576
path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
577
verb = 'BzrDir.create_repository'
1131
path = a_controldir._path_for_remote_call(a_controldir._client)
1132
verb = b'BzrDir.create_repository'
1134
shared_str = b'True'
1136
shared_str = b'False'
583
response = a_bzrdir._call(verb, path, network_name, shared_str)
1138
response = a_controldir._call(verb, path, network_name, shared_str)
584
1139
except errors.UnknownSmartMethod:
585
1140
# Fallback - use vfs methods
586
1141
medium._remember_remote_is_before((1, 13))
587
return self._vfs_initialize(a_bzrdir, shared)
1142
return self._vfs_initialize(a_controldir, shared)
589
1144
# Turn the response into a RemoteRepository object.
590
1145
format = response_tuple_to_repo_format(response[1:])
591
1146
# Used to support creating a real format instance when needed.
592
format._creating_bzrdir = a_bzrdir
593
remote_repo = RemoteRepository(a_bzrdir, format)
1147
format._creating_bzrdir = a_controldir
1148
remote_repo = RemoteRepository(a_controldir, format)
594
1149
format._creating_repo = remote_repo
595
1150
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()
1152
def open(self, a_controldir):
1153
if not isinstance(a_controldir, RemoteBzrDir):
1154
raise AssertionError('%r is not a RemoteBzrDir' % (a_controldir,))
1155
return a_controldir.open_repository()
602
1157
def _ensure_real(self):
603
1158
if self._custom_format is None:
604
self._custom_format = repository.network_format_registry.get(
1160
self._custom_format = _mod_repository.network_format_registry.get(
1163
raise errors.UnknownFormatError(kind='repository',
1164
format=self._network_name)
608
1167
def _fetch_order(self):
909
1517
# TODO: Move to RepositoryBase and unify with the regular Repository
910
1518
# one; unfortunately the tests rely on slightly different behaviour at
911
1519
# present -- mbp 20090710
912
return (self.__class__ is other.__class__ and
913
self.bzrdir.transport.base == other.bzrdir.transport.base)
1520
return (self.__class__ is other.__class__
1521
and self.controldir.transport.base == other.controldir.transport.base)
915
1523
def get_graph(self, other_repository=None):
916
1524
"""Return the graph for this repository format"""
917
1525
parents_provider = self._make_parents_provider(other_repository)
918
1526
return graph.Graph(parents_provider)
921
1528
def get_known_graph_ancestry(self, revision_ids):
922
1529
"""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)
1531
with self.lock_read():
1532
revision_graph = dict(((key, value) for key, value in
1533
self.get_graph().iter_ancestry(revision_ids) if value is not None))
1534
revision_graph = _mod_repository._strip_NULL_ghosts(revision_graph)
1535
return graph.KnownGraph(revision_graph)
929
1537
def gather_stats(self, revid=None, committers=None):
930
1538
"""See Repository.gather_stats()."""
931
path = self.bzrdir._path_for_remote_call(self._client)
1539
path = self.controldir._path_for_remote_call(self._client)
932
1540
# revid can be None to indicate no revisions, not just NULL_REVISION
933
if revid is None or revision.is_null(revid):
1541
if revid is None or _mod_revision.is_null(revid):
936
1544
fmt_revid = revid
937
1545
if committers is None or not committers:
938
fmt_committers = 'no'
1546
fmt_committers = b'no'
940
fmt_committers = 'yes'
1548
fmt_committers = b'yes'
941
1549
response_tuple, response_handler = self._call_expecting_body(
942
'Repository.gather_stats', path, fmt_revid, fmt_committers)
943
if response_tuple[0] != 'ok':
1550
b'Repository.gather_stats', path, fmt_revid, fmt_committers)
1551
if response_tuple[0] != b'ok':
944
1552
raise errors.UnexpectedSmartServerResponse(response_tuple)
946
1554
body = response_handler.read_body_bytes()
948
for line in body.split('\n'):
1556
for line in body.split(b'\n'):
951
key, val_text = line.split(':')
1559
key, val_text = line.split(b':')
1560
key = key.decode('ascii')
952
1561
if key in ('revisions', 'size', 'committers'):
953
1562
result[key] = int(val_text)
954
1563
elif key in ('firstrev', 'latestrev'):
955
values = val_text.split(' ')[1:]
956
result[key] = (float(values[0]), long(values[1]))
1564
values = val_text.split(b' ')[1:]
1565
result[key] = (float(values[0]), int(values[1]))
1195
1846
raise errors.UnexpectedSmartServerResponse(response)
1197
1848
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)
1849
"""Create a descendent repository for new development.
1851
Unlike clone, this does not copy the settings of the repository.
1853
with self.lock_read():
1854
dest_repo = self._create_sprouting_repo(to_bzrdir, shared=False)
1855
dest_repo.fetch(self, revision_id=revision_id)
1858
def _create_sprouting_repo(self, a_controldir, shared):
1859
if not isinstance(a_controldir._format, self.controldir._format.__class__):
1860
# use target default format.
1861
dest_repo = a_controldir.create_repository()
1863
# Most control formats need the repository to be specifically
1864
# created, but on some old all-in-one formats it's not needed
1866
dest_repo = self._format.initialize(
1867
a_controldir, shared=shared)
1868
except errors.UninitializableFormat:
1869
dest_repo = a_controldir.open_repository()
1203
1870
return dest_repo
1205
### These methods are just thin shims to the VFS object for now.
1872
# These methods are just thin shims to the VFS object for now.
1207
1874
def revision_tree(self, revision_id):
1209
return self._real_repository.revision_tree(revision_id)
1875
with self.lock_read():
1876
revision_id = _mod_revision.ensure_null(revision_id)
1877
if revision_id == _mod_revision.NULL_REVISION:
1878
return InventoryRevisionTree(self,
1879
Inventory(root_id=None), _mod_revision.NULL_REVISION)
1881
return list(self.revision_trees([revision_id]))[0]
1211
1883
def get_serializer_format(self):
1213
return self._real_repository.get_serializer_format()
1884
path = self.controldir._path_for_remote_call(self._client)
1886
response = self._call(b'VersionedFileRepository.get_serializer_format',
1888
except errors.UnknownSmartMethod:
1890
return self._real_repository.get_serializer_format()
1891
if response[0] != b'ok':
1892
raise errors.UnexpectedSmartServerResponse(response)
1215
1895
def get_commit_builder(self, branch, parents, config, timestamp=None,
1216
1896
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)
1897
revision_id=None, lossy=False):
1898
"""Obtain a CommitBuilder for this repository.
1900
:param branch: Branch to commit to.
1901
:param parents: Revision ids of the parents of the new revision.
1902
:param config: Configuration to use.
1903
:param timestamp: Optional timestamp recorded for commit.
1904
:param timezone: Optional timezone for timestamp.
1905
:param committer: Optional committer to set for commit.
1906
:param revprops: Optional dictionary of revision properties.
1907
:param revision_id: Optional revision id.
1908
:param lossy: Whether to discard data that can not be natively
1909
represented, when pushing to a foreign VCS
1911
if self._fallback_repositories and not self._format.supports_chks:
1912
raise errors.BzrError("Cannot commit directly to a stacked branch"
1913
" in pre-2a formats. See "
1914
"https://bugs.launchpad.net/bzr/+bug/375013 for details.")
1915
commit_builder_kls = vf_repository.VersionedFileCommitBuilder
1916
result = commit_builder_kls(self, parents, config,
1917
timestamp, timezone, committer, revprops, revision_id,
1919
self.start_write_group()
1227
1922
def add_fallback_repository(self, repository):
1228
1923
"""Add a repository to use for looking up data not held locally.
1266
1962
return self._real_repository.add_inventory(revid, inv, parents)
1268
1964
def add_inventory_by_delta(self, basis_revision_id, delta, new_revision_id,
1269
parents, basis_inv=None, propagate_caches=False):
1965
parents, basis_inv=None, propagate_caches=False):
1270
1966
self._ensure_real()
1271
1967
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)
1968
delta, new_revision_id, parents, basis_inv=basis_inv,
1969
propagate_caches=propagate_caches)
1971
def add_revision(self, revision_id, rev, inv=None):
1972
_mod_revision.check_not_reserved_id(revision_id)
1973
key = (revision_id,)
1974
# check inventory present
1975
if not self.inventories.get_parent_map([key]):
1977
raise errors.WeaveRevisionNotPresent(revision_id,
1980
# yes, this is not suitable for adding with ghosts.
1981
rev.inventory_sha1 = self.add_inventory(revision_id, inv,
1984
rev.inventory_sha1 = self.inventories.get_sha1s([key])[key]
1985
self._add_revision(rev)
1987
def _add_revision(self, rev):
1988
if self._real_repository is not None:
1989
return self._real_repository._add_revision(rev)
1990
text = self._serializer.write_revision_to_string(rev)
1991
key = (rev.revision_id,)
1992
parents = tuple((parent,) for parent in rev.parent_ids)
1993
self._write_group_tokens, missing_keys = self._get_sink().insert_stream(
1994
[('revisions', [FulltextContentFactory(key, parents, None, text)])],
1995
self._format, self._write_group_tokens)
1281
1997
def get_inventory(self, revision_id):
1998
with self.lock_read():
1999
return list(self.iter_inventories([revision_id]))[0]
2001
def _iter_inventories_rpc(self, revision_ids, ordering):
2002
if ordering is None:
2003
ordering = 'unordered'
2004
path = self.controldir._path_for_remote_call(self._client)
2005
body = b"\n".join(revision_ids)
2006
response_tuple, response_handler = (
2007
self._call_with_body_bytes_expecting_body(
2008
b"VersionedFileRepository.get_inventories",
2009
(path, ordering.encode('ascii')), body))
2010
if response_tuple[0] != b"ok":
2011
raise errors.UnexpectedSmartServerResponse(response_tuple)
2012
deserializer = inventory_delta.InventoryDeltaDeserializer()
2013
byte_stream = response_handler.read_streamed_body()
2014
decoded = smart_repo._byte_stream_to_stream(byte_stream)
2016
# no results whatsoever
2018
src_format, stream = decoded
2019
if src_format.network_name() != self._format.network_name():
2020
raise AssertionError(
2021
"Mismatched RemoteRepository and stream src %r, %r" % (
2022
src_format.network_name(), self._format.network_name()))
2023
# ignore the src format, it's not really relevant
2024
prev_inv = Inventory(root_id=None,
2025
revision_id=_mod_revision.NULL_REVISION)
2026
# there should be just one substream, with inventory deltas
2028
substream_kind, substream = next(stream)
2029
except StopIteration:
2031
if substream_kind != "inventory-deltas":
2032
raise AssertionError(
2033
"Unexpected stream %r received" % substream_kind)
2034
for record in substream:
2035
(parent_id, new_id, versioned_root, tree_references, invdelta) = (
2036
deserializer.parse_text_bytes(record.get_bytes_as("fulltext")))
2037
if parent_id != prev_inv.revision_id:
2038
raise AssertionError("invalid base %r != %r" % (parent_id,
2039
prev_inv.revision_id))
2040
inv = prev_inv.create_by_apply_delta(invdelta, new_id)
2041
yield inv, inv.revision_id
2044
def _iter_inventories_vfs(self, revision_ids, ordering=None):
1282
2045
self._ensure_real()
1283
return self._real_repository.get_inventory(revision_id)
2046
return self._real_repository._iter_inventories(revision_ids, ordering)
1285
2048
def iter_inventories(self, revision_ids, ordering=None):
1287
return self._real_repository.iter_inventories(revision_ids, ordering)
2049
"""Get many inventories by revision_ids.
2051
This will buffer some or all of the texts used in constructing the
2052
inventories in memory, but will only parse a single inventory at a
2055
:param revision_ids: The expected revision ids of the inventories.
2056
:param ordering: optional ordering, e.g. 'topological'. If not
2057
specified, the order of revision_ids will be preserved (by
2058
buffering if necessary).
2059
:return: An iterator of inventories.
2061
if ((None in revision_ids) or
2062
(_mod_revision.NULL_REVISION in revision_ids)):
2063
raise ValueError('cannot get null revision inventory')
2064
for inv, revid in self._iter_inventories(revision_ids, ordering):
2066
raise errors.NoSuchRevision(self, revid)
2069
def _iter_inventories(self, revision_ids, ordering=None):
2070
if len(revision_ids) == 0:
2072
missing = set(revision_ids)
2073
if ordering is None:
2074
order_as_requested = True
2076
order = list(revision_ids)
2078
next_revid = order.pop()
2080
order_as_requested = False
2081
if ordering != 'unordered' and self._fallback_repositories:
2082
raise ValueError('unsupported ordering %r' % ordering)
2083
iter_inv_fns = [self._iter_inventories_rpc] + [
2084
fallback._iter_inventories for fallback in
2085
self._fallback_repositories]
2087
for iter_inv in iter_inv_fns:
2088
request = [revid for revid in revision_ids if revid in missing]
2089
for inv, revid in iter_inv(request, ordering):
2092
missing.remove(inv.revision_id)
2093
if ordering != 'unordered':
2097
if order_as_requested:
2098
# Yield as many results as we can while preserving order.
2099
while next_revid in invs:
2100
inv = invs.pop(next_revid)
2101
yield inv, inv.revision_id
2103
next_revid = order.pop()
2105
# We still want to fully consume the stream, just
2106
# in case it is not actually finished at this point
2109
except errors.UnknownSmartMethod:
2110
for inv, revid in self._iter_inventories_vfs(revision_ids, ordering):
2114
if order_as_requested:
2115
if next_revid is not None:
2116
yield None, next_revid
2119
yield invs.get(revid), revid
2122
yield None, missing.pop()
1290
2124
def get_revision(self, revision_id):
1292
return self._real_repository.get_revision(revision_id)
2125
with self.lock_read():
2126
return self.get_revisions([revision_id])[0]
1294
2128
def get_transaction(self):
1295
2129
self._ensure_real()
1296
2130
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)
2132
def clone(self, a_controldir, revision_id=None):
2133
with self.lock_read():
2134
dest_repo = self._create_sprouting_repo(
2135
a_controldir, shared=self.is_shared())
2136
self.copy_content_into(dest_repo, revision_id)
1303
2139
def make_working_trees(self):
1304
2140
"""See Repository.make_working_trees"""
1306
return self._real_repository.make_working_trees()
2141
path = self.controldir._path_for_remote_call(self._client)
2143
response = self._call(b'Repository.make_working_trees', path)
2144
except errors.UnknownSmartMethod:
2146
return self._real_repository.make_working_trees()
2147
if response[0] not in (b'yes', b'no'):
2148
raise SmartProtocolError(
2149
'unexpected response code %s' % (response,))
2150
return response[0] == b'yes'
1308
2152
def refresh_data(self):
1309
"""Re-read any data needed to to synchronise with disk.
2153
"""Re-read any data needed to synchronise with disk.
1311
2155
This method is intended to be called after another repository instance
1312
2156
(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.
2157
repository. On all repositories this will work outside of write groups.
2158
Some repository formats (pack and newer for breezy native formats)
2159
support refresh_data inside write groups. If called inside a write
2160
group on a repository that does not support refreshing in a write group
2161
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
2163
if self._real_repository is not None:
1320
2164
self._real_repository.refresh_data()
2165
# Refresh the parents cache for this object
2166
self._unstacked_provider.disable_cache()
2167
self._unstacked_provider.enable_cache()
1322
2169
def revision_ids_to_search_result(self, result_set):
1323
2170
"""Convert a set of revision ids to a graph SearchResult."""
1324
2171
result_parents = set()
1325
for parents in self.get_graph().get_parent_map(
1326
result_set).itervalues():
2172
for parents in viewvalues(self.get_graph().get_parent_map(result_set)):
1327
2173
result_parents.update(parents)
1328
2174
included_keys = result_set.intersection(result_parents)
1329
2175
start_keys = result_set.difference(included_keys)
1330
2176
exclude_keys = result_parents.difference(result_set)
1331
result = graph.SearchResult(start_keys, exclude_keys,
1332
len(result_set), result_set)
2177
result = vf_search.SearchResult(start_keys, exclude_keys,
2178
len(result_set), result_set)
1336
def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
2181
def search_missing_revision_ids(self, other,
2182
find_ghosts=True, revision_ids=None, if_present_ids=None,
1337
2184
"""Return the revision ids that other has that this does not.
1339
2186
These are returned in topological order.
1341
2188
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)
2190
with self.lock_read():
2191
inter_repo = _mod_repository.InterRepository.get(other, self)
2192
return inter_repo.search_missing_revision_ids(
2193
find_ghosts=find_ghosts, revision_ids=revision_ids,
2194
if_present_ids=if_present_ids, limit=limit)
1346
def fetch(self, source, revision_id=None, pb=None, find_ghosts=False,
2196
def fetch(self, source, revision_id=None, find_ghosts=False,
1348
2198
# No base implementation to use as RemoteRepository is not a subclass
1349
2199
# of Repository; so this is a copy of Repository.fetch().
1350
2200
if fetch_spec is not None and revision_id is not None:
1388
2237
return self._real_repository._get_versioned_file_checker(
1389
2238
revisions, revision_versions_cache)
2240
def _iter_files_bytes_rpc(self, desired_files, absent):
2241
path = self.controldir._path_for_remote_call(self._client)
2244
for (file_id, revid, identifier) in desired_files:
2245
lines.append(b''.join([
2246
osutils.safe_file_id(file_id),
2248
osutils.safe_revision_id(revid)]))
2249
identifiers.append(identifier)
2250
(response_tuple, response_handler) = (
2251
self._call_with_body_bytes_expecting_body(
2252
b"Repository.iter_files_bytes", (path, ), b"\n".join(lines)))
2253
if response_tuple != (b'ok', ):
2254
response_handler.cancel_read_body()
2255
raise errors.UnexpectedSmartServerResponse(response_tuple)
2256
byte_stream = response_handler.read_streamed_body()
2258
def decompress_stream(start, byte_stream, unused):
2259
decompressor = zlib.decompressobj()
2260
yield decompressor.decompress(start)
2261
while decompressor.unused_data == b"":
2263
data = next(byte_stream)
2264
except StopIteration:
2266
yield decompressor.decompress(data)
2267
yield decompressor.flush()
2268
unused.append(decompressor.unused_data)
2271
while b"\n" not in unused:
2273
unused += next(byte_stream)
2274
except StopIteration:
2276
header, rest = unused.split(b"\n", 1)
2277
args = header.split(b"\0")
2278
if args[0] == b"absent":
2279
absent[identifiers[int(args[3])]] = (args[1], args[2])
2282
elif args[0] == b"ok":
2285
raise errors.UnexpectedSmartServerResponse(args)
2287
yield (identifiers[idx],
2288
decompress_stream(rest, byte_stream, unused_chunks))
2289
unused = b"".join(unused_chunks)
1391
2291
def iter_files_bytes(self, desired_files):
1392
2292
"""See Repository.iter_file_bytes.
1395
return self._real_repository.iter_files_bytes(desired_files)
2296
for (identifier, bytes_iterator) in self._iter_files_bytes_rpc(
2297
desired_files, absent):
2298
yield identifier, bytes_iterator
2299
for fallback in self._fallback_repositories:
2302
desired_files = [(key[0], key[1], identifier)
2303
for identifier, key in viewitems(absent)]
2304
for (identifier, bytes_iterator) in fallback.iter_files_bytes(desired_files):
2305
del absent[identifier]
2306
yield identifier, bytes_iterator
2308
# There may be more missing items, but raise an exception
2310
missing_identifier = next(iter(absent))
2311
missing_key = absent[missing_identifier]
2312
raise errors.RevisionNotPresent(revision_id=missing_key[1],
2313
file_id=missing_key[0])
2314
except errors.UnknownSmartMethod:
2316
for (identifier, bytes_iterator) in (
2317
self._real_repository.iter_files_bytes(desired_files)):
2318
yield identifier, bytes_iterator
2320
def get_cached_parent_map(self, revision_ids):
2321
"""See breezy.CachingParentsProvider.get_cached_parent_map"""
2322
return self._unstacked_provider.get_cached_parent_map(revision_ids)
1397
2324
def get_parent_map(self, revision_ids):
1398
"""See bzrlib.Graph.get_parent_map()."""
2325
"""See breezy.Graph.get_parent_map()."""
1399
2326
return self._make_parents_provider().get_parent_map(revision_ids)
1401
2328
def _get_parent_map_rpc(self, keys):
1529
2445
revision_graph[d[0]] = (NULL_REVISION,)
1530
2446
return revision_graph
1533
2448
def get_signature_text(self, revision_id):
1535
return self._real_repository.get_signature_text(revision_id)
2449
with self.lock_read():
2450
path = self.controldir._path_for_remote_call(self._client)
2452
response_tuple, response_handler = self._call_expecting_body(
2453
b'Repository.get_revision_signature_text', path, revision_id)
2454
except errors.UnknownSmartMethod:
2456
return self._real_repository.get_signature_text(revision_id)
2457
except errors.NoSuchRevision as err:
2458
for fallback in self._fallback_repositories:
2460
return fallback.get_signature_text(revision_id)
2461
except errors.NoSuchRevision:
2465
if response_tuple[0] != b'ok':
2466
raise errors.UnexpectedSmartServerResponse(response_tuple)
2467
return response_handler.read_body_bytes()
1538
2469
def _get_inventory_xml(self, revision_id):
1540
return self._real_repository._get_inventory_xml(revision_id)
2470
with self.lock_read():
2471
# This call is used by older working tree formats,
2472
# which stored a serialized basis inventory.
2474
return self._real_repository._get_inventory_xml(revision_id)
1542
2476
def reconcile(self, other=None, thorough=False):
1544
return self._real_repository.reconcile(other=other, thorough=thorough)
2477
from ..reconcile import RepoReconciler
2478
with self.lock_write():
2479
path = self.controldir._path_for_remote_call(self._client)
2481
response, handler = self._call_expecting_body(
2482
b'Repository.reconcile', path, self._lock_token)
2483
except (errors.UnknownSmartMethod, errors.TokenLockingNotSupported):
2485
return self._real_repository.reconcile(other=other, thorough=thorough)
2486
if response != (b'ok', ):
2487
raise errors.UnexpectedSmartServerResponse(response)
2488
body = handler.read_body_bytes()
2489
result = RepoReconciler(self)
2490
for line in body.split(b'\n'):
2493
key, val_text = line.split(b':')
2494
if key == b"garbage_inventories":
2495
result.garbage_inventories = int(val_text)
2496
elif key == b"inconsistent_parents":
2497
result.inconsistent_parents = int(val_text)
2499
mutter("unknown reconcile key %r" % key)
1546
2502
def all_revision_ids(self):
1548
return self._real_repository.all_revision_ids()
2503
path = self.controldir._path_for_remote_call(self._client)
2505
response_tuple, response_handler = self._call_expecting_body(
2506
b"Repository.all_revision_ids", path)
2507
except errors.UnknownSmartMethod:
2509
return self._real_repository.all_revision_ids()
2510
if response_tuple != (b"ok", ):
2511
raise errors.UnexpectedSmartServerResponse(response_tuple)
2512
revids = set(response_handler.read_body_bytes().splitlines())
2513
for fallback in self._fallback_repositories:
2514
revids.update(set(fallback.all_revision_ids()))
2517
def _filtered_revision_trees(self, revision_ids, file_ids):
2518
"""Return Tree for a revision on this branch with only some files.
2520
:param revision_ids: a sequence of revision-ids;
2521
a revision-id may not be None or b'null:'
2522
:param file_ids: if not None, the result is filtered
2523
so that only those file-ids, their parents and their
2524
children are included.
2526
inventories = self.iter_inventories(revision_ids)
2527
for inv in inventories:
2528
# Should we introduce a FilteredRevisionTree class rather
2529
# than pre-filter the inventory here?
2530
filtered_inv = inv.filter(file_ids)
2531
yield InventoryRevisionTree(self, filtered_inv, filtered_inv.revision_id)
1551
2533
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)
2534
with self.lock_read():
2535
medium = self._client._medium
2536
if medium._is_remote_before((1, 2)):
2538
for delta in self._real_repository.get_deltas_for_revisions(
2539
revisions, specific_fileids):
2542
# Get the revision-ids of interest
2543
required_trees = set()
2544
for revision in revisions:
2545
required_trees.add(revision.revision_id)
2546
required_trees.update(revision.parent_ids[:1])
2548
# Get the matching filtered trees. Note that it's more
2549
# efficient to pass filtered trees to changes_from() rather
2550
# than doing the filtering afterwards. changes_from() could
2551
# arguably do the filtering itself but it's path-based, not
2552
# file-id based, so filtering before or afterwards is
2554
if specific_fileids is None:
2555
trees = dict((t.get_revision_id(), t) for
2556
t in self.revision_trees(required_trees))
2558
trees = dict((t.get_revision_id(), t) for
2559
t in self._filtered_revision_trees(required_trees,
2562
# Calculate the deltas
2563
for revision in revisions:
2564
if not revision.parent_ids:
2565
old_tree = self.revision_tree(_mod_revision.NULL_REVISION)
2567
old_tree = trees[revision.parent_ids[0]]
2568
yield trees[revision.revision_id].changes_from(old_tree)
1557
2570
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)
2571
with self.lock_read():
2572
r = self.get_revision(revision_id)
2573
return list(self.get_deltas_for_revisions([r],
2574
specific_fileids=specific_fileids))[0]
1563
2576
def revision_trees(self, revision_ids):
1565
return self._real_repository.revision_trees(revision_ids)
2577
with self.lock_read():
2578
inventories = self.iter_inventories(revision_ids)
2579
for inv in inventories:
2580
yield RemoteInventoryTree(self, inv, inv.revision_id)
1568
2582
def get_revision_reconcile(self, revision_id):
1570
return self._real_repository.get_revision_reconcile(revision_id)
2583
with self.lock_read():
2585
return self._real_repository.get_revision_reconcile(revision_id)
1573
2587
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)
2588
with self.lock_read():
2590
return self._real_repository.check(revision_ids=revision_ids,
2591
callback_refs=callback_refs, check_repo=check_repo)
1578
2593
def copy_content_into(self, destination, revision_id=None):
1580
return self._real_repository.copy_content_into(
1581
destination, revision_id=revision_id)
2594
"""Make a complete copy of the content in self into destination.
2596
This is a destructive operation! Do not use it on existing
2599
interrepo = _mod_repository.InterRepository.get(self, destination)
2600
return interrepo.copy_content(revision_id)
1583
2602
def _copy_repository_tarball(self, to_bzrdir, revision_id=None):
1584
2603
# get a tarball of the remote repository, and copy from that into the
1586
from bzrlib import osutils
1588
2606
# TODO: Maybe a progress bar while streaming the tarball?
1589
note("Copying repository content as tarball...")
2607
note(gettext("Copying repository content as tarball..."))
1590
2608
tar_file = self._get_tarball('bz2')
1591
2609
if tar_file is None:
1593
2611
destination = to_bzrdir.create_repository()
1595
2613
tar = tarfile.open('repository', fileobj=tar_file,
1597
2615
tmpdir = osutils.mkdtemp()
1599
_extract_tar(tar, tmpdir)
1600
tmp_bzrdir = BzrDir.open(tmpdir)
2617
tar.extractall(tmpdir)
2618
tmp_bzrdir = _mod_bzrdir.BzrDir.open(tmpdir)
1601
2619
tmp_repo = tmp_bzrdir.open_repository()
1602
2620
tmp_repo.copy_content_into(destination, revision_id)
1680
2709
self._ensure_real()
1681
2710
return self._real_repository.texts
1684
def get_revisions(self, revision_ids):
1686
return self._real_repository.get_revisions(revision_ids)
2712
def _iter_revisions_rpc(self, revision_ids):
2713
body = b"\n".join(revision_ids)
2714
path = self.controldir._path_for_remote_call(self._client)
2715
response_tuple, response_handler = (
2716
self._call_with_body_bytes_expecting_body(
2717
b"Repository.iter_revisions", (path, ), body))
2718
if response_tuple[0] != b"ok":
2719
raise errors.UnexpectedSmartServerResponse(response_tuple)
2720
serializer_format = response_tuple[1].decode('ascii')
2721
serializer = serializer_format_registry.get(serializer_format)
2722
byte_stream = response_handler.read_streamed_body()
2723
decompressor = zlib.decompressobj()
2725
for bytes in byte_stream:
2726
chunks.append(decompressor.decompress(bytes))
2727
if decompressor.unused_data != b"":
2728
chunks.append(decompressor.flush())
2729
yield serializer.read_revision_from_string(b"".join(chunks))
2730
unused = decompressor.unused_data
2731
decompressor = zlib.decompressobj()
2732
chunks = [decompressor.decompress(unused)]
2733
chunks.append(decompressor.flush())
2734
text = b"".join(chunks)
2736
yield serializer.read_revision_from_string(b"".join(chunks))
2738
def iter_revisions(self, revision_ids):
2739
for rev_id in revision_ids:
2740
if not rev_id or not isinstance(rev_id, bytes):
2741
raise errors.InvalidRevisionId(
2742
revision_id=rev_id, branch=self)
2743
with self.lock_read():
2745
missing = set(revision_ids)
2746
for rev in self._iter_revisions_rpc(revision_ids):
2747
missing.remove(rev.revision_id)
2748
yield (rev.revision_id, rev)
2749
for fallback in self._fallback_repositories:
2752
for (revid, rev) in fallback.iter_revisions(missing):
2755
missing.remove(revid)
2756
for revid in missing:
2758
except errors.UnknownSmartMethod:
2760
for entry in self._real_repository.iter_revisions(revision_ids):
1688
2763
def supports_rich_root(self):
1689
2764
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
2767
def _serializer(self):
1697
2768
return self._format._serializer
1699
2770
def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
1701
return self._real_repository.store_revision_signature(
1702
gpg_strategy, plaintext, revision_id)
2771
with self.lock_write():
2772
signature = gpg_strategy.sign(plaintext, gpg.MODE_CLEAR)
2773
self.add_signature_text(revision_id, signature)
1704
2775
def add_signature_text(self, revision_id, signature):
1706
return self._real_repository.add_signature_text(revision_id, signature)
2776
if self._real_repository:
2777
# If there is a real repository the write group will
2778
# be in the real repository as well, so use that:
2780
return self._real_repository.add_signature_text(
2781
revision_id, signature)
2782
path = self.controldir._path_for_remote_call(self._client)
2783
response, handler = self._call_with_body_bytes_expecting_body(
2784
b'Repository.add_signature_text', (path, self._lock_token,
2786
tuple([token.encode('utf-8')
2787
for token in self._write_group_tokens]),
2789
handler.cancel_read_body()
2791
if response[0] != b'ok':
2792
raise errors.UnexpectedSmartServerResponse(response)
2793
self._write_group_tokens = [token.decode(
2794
'utf-8') for token in response[1:]]
1708
2796
def has_signature_for_revision_id(self, revision_id):
1710
return self._real_repository.has_signature_for_revision_id(revision_id)
2797
path = self.controldir._path_for_remote_call(self._client)
2799
response = self._call(b'Repository.has_signature_for_revision_id',
2801
except errors.UnknownSmartMethod:
2803
return self._real_repository.has_signature_for_revision_id(
2805
if response[0] not in (b'yes', b'no'):
2806
raise SmartProtocolError(
2807
'unexpected response code %s' % (response,))
2808
if response[0] == b'yes':
2810
for fallback in self._fallback_repositories:
2811
if fallback.has_signature_for_revision_id(revision_id):
2815
def verify_revision_signature(self, revision_id, gpg_strategy):
2816
with self.lock_read():
2817
if not self.has_signature_for_revision_id(revision_id):
2818
return gpg.SIGNATURE_NOT_SIGNED, None
2819
signature = self.get_signature_text(revision_id)
2821
testament = _mod_testament.Testament.from_revision(
2824
(status, key, signed_plaintext) = gpg_strategy.verify(signature)
2825
if testament.as_short_text() != signed_plaintext:
2826
return gpg.SIGNATURE_NOT_VALID, None
2827
return (status, key)
1712
2829
def item_keys_introduced_by(self, revision_ids, _files_pb=None):
1713
2830
self._ensure_real()
1714
2831
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()
2832
_files_pb=_files_pb)
1722
2834
def _find_inconsistent_revision_parents(self, revisions_iterator=None):
1723
2835
self._ensure_real()
1742
2853
:param recipe: A search recipe (start, stop, count).
1743
2854
: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))
2856
start_keys = b' '.join(recipe[1])
2857
stop_keys = b' '.join(recipe[2])
2858
count = str(recipe[3]).encode('ascii')
2859
return b'\n'.join((start_keys, stop_keys, count))
1750
2861
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)
2862
parts = search_result.get_network_struct()
2863
return b'\n'.join(parts)
1759
2865
def autopack(self):
1760
path = self.bzrdir._path_for_remote_call(self._client)
2866
path = self.controldir._path_for_remote_call(self._client)
1762
response = self._call('PackRepository.autopack', path)
2868
response = self._call(b'PackRepository.autopack', path)
1763
2869
except errors.UnknownSmartMethod:
1764
2870
self._ensure_real()
1765
2871
self._real_repository._pack_collection.autopack()
1767
2873
self.refresh_data()
1768
if response[0] != 'ok':
1769
raise errors.UnexpectedSmartServerResponse(response)
1772
class RemoteStreamSink(repository.StreamSink):
2874
if response[0] != b'ok':
2875
raise errors.UnexpectedSmartServerResponse(response)
2877
def _revision_archive(self, revision_id, format, name, root, subdir,
2879
path = self.controldir._path_for_remote_call(self._client)
2880
format = format or ''
2882
subdir = subdir or ''
2883
force_mtime = int(force_mtime) if force_mtime is not None else None
2885
response, protocol = self._call_expecting_body(
2886
b'Repository.revision_archive', path,
2888
format.encode('ascii'),
2889
os.path.basename(name).encode('utf-8'),
2890
root.encode('utf-8'),
2891
subdir.encode('utf-8'),
2893
except errors.UnknownSmartMethod:
2895
if response[0] == b'ok':
2896
return iter([protocol.read_body_bytes()])
2897
raise errors.UnexpectedSmartServerResponse(response)
2899
def _annotate_file_revision(self, revid, tree_path, file_id, default_revision):
2900
path = self.controldir._path_for_remote_call(self._client)
2901
tree_path = tree_path.encode('utf-8')
2902
file_id = file_id or b''
2903
default_revision = default_revision or b''
2905
response, handler = self._call_expecting_body(
2906
b'Repository.annotate_file_revision', path,
2907
revid, tree_path, file_id, default_revision)
2908
except errors.UnknownSmartMethod:
2910
if response[0] != b'ok':
2911
raise errors.UnexpectedSmartServerResponse(response)
2912
return map(tuple, bencode.bdecode(handler.read_body_bytes()))
2915
class RemoteStreamSink(vf_repository.StreamSink):
1774
2917
def _insert_real(self, stream, src_format, resume_tokens):
1775
2918
self.target_repo._ensure_real()
2059
3262
def network_name(self):
2060
3263
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)
3265
def open(self, a_controldir, name=None, ignore_fallbacks=False):
3266
return a_controldir.open_branch(name=name,
3267
ignore_fallbacks=ignore_fallbacks)
2066
def _vfs_initialize(self, a_bzrdir, name):
3269
def _vfs_initialize(self, a_controldir, name, append_revisions_only,
2067
3271
# Initialisation when using a local bzrdir object, or a non-vfs init
2068
3272
# method is not available on the server.
2069
3273
# 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,
3275
if isinstance(a_controldir, RemoteBzrDir):
3276
a_controldir._ensure_real()
3277
result = self._custom_format.initialize(a_controldir._real_bzrdir,
3278
name=name, append_revisions_only=append_revisions_only,
3279
repository=repository)
2076
3281
# 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,
3282
result = self._custom_format.initialize(a_controldir, name=name,
3283
append_revisions_only=append_revisions_only,
3284
repository=repository)
3285
if (isinstance(a_controldir, RemoteBzrDir)
3286
and not isinstance(result, RemoteBranch)):
3287
result = RemoteBranch(a_controldir, a_controldir.find_repository(), result,
2084
def initialize(self, a_bzrdir, name=None):
3291
def initialize(self, a_controldir, name=None, repository=None,
3292
append_revisions_only=None):
3294
name = a_controldir._get_selected_branch()
2085
3295
# 1) get the network name to use.
2086
3296
if self._custom_format:
2087
3297
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')()
3299
# Select the current breezy default and ask for that.
3300
reference_bzrdir_format = controldir.format_registry.get(
2091
3302
reference_format = reference_bzrdir_format.get_branch_format()
2092
3303
self._custom_format = reference_format
2093
3304
network_name = reference_format.network_name()
2094
3305
# 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
3306
if not isinstance(a_controldir, RemoteBzrDir):
3307
return self._vfs_initialize(a_controldir, name=name,
3308
append_revisions_only=append_revisions_only,
3309
repository=repository)
3310
medium = a_controldir._client._medium
2098
3311
if medium._is_remote_before((1, 13)):
2099
return self._vfs_initialize(a_bzrdir, name=name)
3312
return self._vfs_initialize(a_controldir, name=name,
3313
append_revisions_only=append_revisions_only,
3314
repository=repository)
2100
3315
# Creating on a remote bzr dir.
2101
3316
# 2) try direct creation via RPC
2102
path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
2103
if name is not None:
3317
path = a_controldir._path_for_remote_call(a_controldir._client)
2104
3319
# XXX JRV20100304: Support creating colocated branches
2105
3320
raise errors.NoColocatedBranchSupport(self)
2106
verb = 'BzrDir.create_branch'
3321
verb = b'BzrDir.create_branch'
2108
response = a_bzrdir._call(verb, path, network_name)
3323
response = a_controldir._call(verb, path, network_name)
2109
3324
except errors.UnknownSmartMethod:
2110
3325
# Fallback - use vfs methods
2111
3326
medium._remember_remote_is_before((1, 13))
2112
return self._vfs_initialize(a_bzrdir, name=name)
2113
if response[0] != 'ok':
3327
return self._vfs_initialize(a_controldir, name=name,
3328
append_revisions_only=append_revisions_only,
3329
repository=repository)
3330
if response[0] != b'ok':
2114
3331
raise errors.UnexpectedSmartServerResponse(response)
2115
3332
# Turn the response into a RemoteRepository object.
2116
3333
format = RemoteBranchFormat(network_name=response[1])
2117
3334
repo_format = response_tuple_to_repo_format(response[3:])
2118
if response[2] == '':
2119
repo_bzrdir = a_bzrdir
3335
repo_path = response[2].decode('utf-8')
3336
if repository is not None:
3337
remote_repo_url = urlutils.join(a_controldir.user_url, repo_path)
3338
url_diff = urlutils.relative_url(repository.user_url,
3341
raise AssertionError(
3342
'repository.user_url %r does not match URL from server '
3343
'response (%r + %r)'
3344
% (repository.user_url, a_controldir.user_url, repo_path))
3345
remote_repo = repository
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)
3348
repo_bzrdir = a_controldir
3350
repo_bzrdir = RemoteBzrDir(
3351
a_controldir.root_transport.clone(
3352
repo_path), a_controldir._format,
3353
a_controldir._client)
3354
remote_repo = RemoteRepository(repo_bzrdir, repo_format)
3355
remote_branch = RemoteBranch(a_controldir, remote_repo,
3356
format=format, setup_stacking=False, name=name)
3357
if append_revisions_only:
3358
remote_branch.set_append_revisions_only(append_revisions_only)
2127
3359
# XXX: We know this is a new branch, so it must have revno 0, revid
2128
3360
# NULL_REVISION. Creating the branch locked would make this be unable
2129
3361
# to be wrong; here its simply very unlikely to be wrong. RBC 20090225
2148
3380
self._ensure_real()
2149
3381
return self._custom_format.supports_set_append_revisions_only()
3383
def _use_default_local_heads_to_fetch(self):
3384
# If the branch format is a metadir format *and* its heads_to_fetch
3385
# implementation is not overridden vs the base class, we can use the
3386
# base class logic rather than use the heads_to_fetch RPC. This is
3387
# usually cheaper in terms of net round trips, as the last-revision and
3388
# tags info fetched is cached and would be fetched anyway.
3390
if isinstance(self._custom_format, bzrbranch.BranchFormatMetadir):
3391
branch_class = self._custom_format._branch_class()
3392
heads_to_fetch_impl = get_unbound_function(
3393
branch_class.heads_to_fetch)
3394
if heads_to_fetch_impl is get_unbound_function(branch.Branch.heads_to_fetch):
3399
class RemoteBranchStore(_mod_config.IniFileStore):
3400
"""Branch store which attempts to use HPSS calls to retrieve branch store.
3402
Note that this is specific to bzr-based formats.
3405
def __init__(self, branch):
3406
super(RemoteBranchStore, self).__init__()
3407
self.branch = branch
3409
self._real_store = None
3411
def external_url(self):
3412
return urlutils.join(self.branch.user_url, 'branch.conf')
3414
def _load_content(self):
3415
path = self.branch._remote_path()
3417
response, handler = self.branch._call_expecting_body(
3418
b'Branch.get_config_file', path)
3419
except errors.UnknownSmartMethod:
3421
return self._real_store._load_content()
3422
if len(response) and response[0] != b'ok':
3423
raise errors.UnexpectedSmartServerResponse(response)
3424
return handler.read_body_bytes()
3426
def _save_content(self, content):
3427
path = self.branch._remote_path()
3429
response, handler = self.branch._call_with_body_bytes_expecting_body(
3430
b'Branch.put_config_file', (path,
3431
self.branch._lock_token, self.branch._repo_lock_token),
3433
except errors.UnknownSmartMethod:
3435
return self._real_store._save_content(content)
3436
handler.cancel_read_body()
3437
if response != (b'ok', ):
3438
raise errors.UnexpectedSmartServerResponse(response)
3440
def _ensure_real(self):
3441
self.branch._ensure_real()
3442
if self._real_store is None:
3443
self._real_store = _mod_config.BranchStore(self.branch)
2152
3446
class RemoteBranch(branch.Branch, _RpcHelper, lock._RelockDebugMixin):
2153
3447
"""Branch stored on a server accessed by HPSS RPC.
2644
3995
self._ensure_real()
2645
3996
return self._real_branch._set_parent_location(url)
2648
3998
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)
4000
with self.lock_write():
4001
self._clear_cached_state_of_remote_branch_only()
4003
return self._real_branch.pull(
4004
source, overwrite=overwrite, stop_revision=stop_revision,
4005
_override_hook_target=self, **kwargs)
4007
def push(self, target, overwrite=False, stop_revision=None, lossy=False):
4008
with self.lock_read():
4010
return self._real_branch.push(
4011
target, overwrite=overwrite, stop_revision=stop_revision, lossy=lossy,
4012
_override_hook_source_branch=self)
4014
def peek_lock_mode(self):
4015
return self._lock_mode
2663
4017
def is_locked(self):
2664
4018
return self._lock_count >= 1
4020
def revision_id_to_dotted_revno(self, revision_id):
4021
"""Given a revision id, return its dotted revno.
4023
:return: a tuple like (1,) or (400,1,3).
4025
with self.lock_read():
4027
response = self._call(b'Branch.revision_id_to_revno',
4028
self._remote_path(), revision_id)
4029
except errors.UnknownSmartMethod:
4031
return self._real_branch.revision_id_to_dotted_revno(revision_id)
4032
if response[0] == b'ok':
4033
return tuple([int(x) for x in response[1:]])
4035
raise errors.UnexpectedSmartServerResponse(response)
2667
4037
def revision_id_to_revno(self, revision_id):
2669
return self._real_branch.revision_id_to_revno(revision_id)
4038
"""Given a revision id on the branch mainline, return its revno.
4042
with self.lock_read():
4044
response = self._call(b'Branch.revision_id_to_revno',
4045
self._remote_path(), revision_id)
4046
except errors.UnknownSmartMethod:
4048
return self._real_branch.revision_id_to_revno(revision_id)
4049
if response[0] == b'ok':
4050
if len(response) == 2:
4051
return int(response[1])
4052
raise NoSuchRevision(self, revision_id)
4054
raise errors.UnexpectedSmartServerResponse(response)
2672
4056
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)
4057
with self.lock_write():
4058
# XXX: These should be returned by the set_last_revision_info verb
4059
old_revno, old_revid = self.last_revision_info()
4060
self._run_pre_change_branch_tip_hooks(revno, revision_id)
4061
if not revision_id or not isinstance(revision_id, bytes):
4062
raise errors.InvalidRevisionId(
4063
revision_id=revision_id, branch=self)
4065
response = self._call(b'Branch.set_last_revision_info',
4066
self._remote_path(), self._lock_token, self._repo_lock_token,
4067
str(revno).encode('ascii'), revision_id)
4068
except errors.UnknownSmartMethod:
4070
self._clear_cached_state_of_remote_branch_only()
4071
self._real_branch.set_last_revision_info(revno, revision_id)
4072
self._last_revision_info_cache = revno, revision_id
4074
if response == (b'ok',):
4075
self._clear_cached_state()
4076
self._last_revision_info_cache = revno, revision_id
4077
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
4078
# Update the _real_branch's cache too.
4079
if self._real_branch is not None:
4080
cache = self._last_revision_info_cache
4081
self._real_branch._last_revision_info_cache = cache
4083
raise errors.UnexpectedSmartServerResponse(response)
2699
4085
def generate_revision_history(self, revision_id, last_rev=None,
2700
4086
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))
4087
with self.lock_write():
4088
medium = self._client._medium
4089
if not medium._is_remote_before((1, 6)):
4090
# Use a smart method for 1.6 and above servers
4092
self._set_last_revision_descendant(revision_id, other_branch,
4093
allow_diverged=True, allow_overwrite_descendant=True)
4095
except errors.UnknownSmartMethod:
4096
medium._remember_remote_is_before((1, 6))
4097
self._clear_cached_state_of_remote_branch_only()
4098
graph = self.repository.get_graph()
4099
(last_revno, last_revid) = self.last_revision_info()
4100
known_revision_ids = [
4101
(last_revid, last_revno),
4102
(_mod_revision.NULL_REVISION, 0),
4104
if last_rev is not None:
4105
if not graph.is_ancestor(last_rev, revision_id):
4106
# our previous tip is not merged into stop_revision
4107
raise errors.DivergedBranches(self, other_branch)
4108
revno = graph.find_distance_to_null(
4109
revision_id, known_revision_ids)
4110
self.set_last_revision_info(revno, revision_id)
2714
4112
def set_push_location(self, location):
4113
self._set_config_location('push_location', location)
4115
def heads_to_fetch(self):
4116
if self._format._use_default_local_heads_to_fetch():
4117
# We recognise this format, and its heads-to-fetch implementation
4118
# is the default one (tip + tags). In this case it's cheaper to
4119
# just use the default implementation rather than a special RPC as
4120
# the tip and tags data is cached.
4121
return branch.Branch.heads_to_fetch(self)
4122
medium = self._client._medium
4123
if medium._is_remote_before((2, 4)):
4124
return self._vfs_heads_to_fetch()
4126
return self._rpc_heads_to_fetch()
4127
except errors.UnknownSmartMethod:
4128
medium._remember_remote_is_before((2, 4))
4129
return self._vfs_heads_to_fetch()
4131
def _rpc_heads_to_fetch(self):
4132
response = self._call(b'Branch.heads_to_fetch', self._remote_path())
4133
if len(response) != 2:
4134
raise errors.UnexpectedSmartServerResponse(response)
4135
must_fetch, if_present_fetch = response
4136
return set(must_fetch), set(if_present_fetch)
4138
def _vfs_heads_to_fetch(self):
2715
4139
self._ensure_real()
2716
return self._real_branch.set_push_location(location)
4140
return self._real_branch.heads_to_fetch()
2719
4143
class RemoteConfig(object):
2774
4208
medium = self._branch._client._medium
2775
4209
if medium._is_remote_before((1, 14)):
2776
4210
return self._vfs_set_option(value, name, section)
2778
path = self._branch._remote_path()
2779
response = self._branch._client.call('Branch.set_config_option',
4211
if isinstance(value, dict):
4212
if medium._is_remote_before((2, 2)):
4213
return self._vfs_set_option(value, name, section)
4214
return self._set_config_option_dict(value, name, section)
4216
return self._set_config_option(value, name, section)
4218
def _set_config_option(self, value, name, section):
4220
path = self._branch._remote_path()
4221
response = self._branch._client.call(b'Branch.set_config_option',
4222
path, self._branch._lock_token, self._branch._repo_lock_token,
4224
'utf8'), name.encode('utf-8'),
4225
(section or '').encode('utf-8'))
4226
except errors.UnknownSmartMethod:
4227
medium = self._branch._client._medium
4228
medium._remember_remote_is_before((1, 14))
4229
return self._vfs_set_option(value, name, section)
4231
raise errors.UnexpectedSmartServerResponse(response)
4233
def _serialize_option_dict(self, option_dict):
4235
for key, value in option_dict.items():
4236
if isinstance(key, text_type):
4237
key = key.encode('utf8')
4238
if isinstance(value, text_type):
4239
value = value.encode('utf8')
4240
utf8_dict[key] = value
4241
return bencode.bencode(utf8_dict)
4243
def _set_config_option_dict(self, value, name, section):
4245
path = self._branch._remote_path()
4246
serialised_dict = self._serialize_option_dict(value)
4247
response = self._branch._client.call(
4248
b'Branch.set_config_option_dict',
2780
4249
path, self._branch._lock_token, self._branch._repo_lock_token,
2781
value.encode('utf8'), name, section or '')
4250
serialised_dict, name.encode('utf-8'), (section or '').encode('utf-8'))
2782
4251
except errors.UnknownSmartMethod:
2783
medium._remember_remote_is_before((1, 14))
4252
medium = self._branch._client._medium
4253
medium._remember_remote_is_before((2, 2))
2784
4254
return self._vfs_set_option(value, name, section)
2785
4255
if response != ():
2786
4256
raise errors.UnexpectedSmartServerResponse(response)
2856
4320
def find(name):
2858
4322
return context[name]
2859
except KeyError, key_err:
2860
mutter('Missing key %r in context %r', key_err.args[0], context)
4324
mutter('Missing key \'%s\' in context %r', name, context)
2862
4327
def get_path():
2863
4328
"""Get the path from the context if present, otherwise use first error
2867
4332
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)
4335
return err.error_args[0].decode('utf-8')
4337
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)
4339
if not isinstance(err.error_verb, bytes):
4340
raise TypeError(err.error_verb)
4342
translator = error_translators.get(err.error_verb)
4346
raise translator(err, find, get_path)
4348
translator = no_context_error_translators.get(err.error_verb)
4350
raise errors.UnknownErrorFromSmartServer(err)
4352
raise translator(err)
4355
error_translators.register(b'NoSuchRevision',
4356
lambda err, find, get_path: NoSuchRevision(
4357
find('branch'), err.error_args[0]))
4358
error_translators.register(b'nosuchrevision',
4359
lambda err, find, get_path: NoSuchRevision(
4360
find('repository'), err.error_args[0]))
4363
def _translate_nobranch_error(err, find, get_path):
4364
if len(err.error_args) >= 1:
4365
extra = err.error_args[0].decode('utf-8')
4368
return errors.NotBranchError(path=find('bzrdir').root_transport.base,
4372
error_translators.register(b'nobranch', _translate_nobranch_error)
4373
error_translators.register(b'norepository',
4374
lambda err, find, get_path: errors.NoRepositoryPresent(
4376
error_translators.register(b'UnlockableTransport',
4377
lambda err, find, get_path: errors.UnlockableTransport(
4378
find('bzrdir').root_transport))
4379
error_translators.register(b'TokenMismatch',
4380
lambda err, find, get_path: errors.TokenMismatch(
4381
find('token'), '(remote token)'))
4382
error_translators.register(b'Diverged',
4383
lambda err, find, get_path: errors.DivergedBranches(
4384
find('branch'), find('other_branch')))
4385
error_translators.register(b'NotStacked',
4386
lambda err, find, get_path: errors.NotStacked(branch=find('branch')))
4389
def _translate_PermissionDenied(err, find, get_path):
4391
if len(err.error_args) >= 2:
4392
extra = err.error_args[1].decode('utf-8')
4395
return errors.PermissionDenied(path, extra=extra)
4398
error_translators.register(b'PermissionDenied', _translate_PermissionDenied)
4399
error_translators.register(b'ReadError',
4400
lambda err, find, get_path: errors.ReadError(get_path()))
4401
error_translators.register(b'NoSuchFile',
4402
lambda err, find, get_path: errors.NoSuchFile(get_path()))
4403
error_translators.register(b'TokenLockingNotSupported',
4404
lambda err, find, get_path: errors.TokenLockingNotSupported(
4405
find('repository')))
4406
error_translators.register(b'UnsuspendableWriteGroup',
4407
lambda err, find, get_path: errors.UnsuspendableWriteGroup(
4408
repository=find('repository')))
4409
error_translators.register(b'UnresumableWriteGroup',
4410
lambda err, find, get_path: errors.UnresumableWriteGroup(
4411
repository=find('repository'), write_groups=err.error_args[0],
4412
reason=err.error_args[1]))
4413
no_context_error_translators.register(b'GhostRevisionsHaveNoRevno',
4414
lambda err: errors.GhostRevisionsHaveNoRevno(*err.error_args))
4415
no_context_error_translators.register(b'IncompatibleRepositories',
4416
lambda err: errors.IncompatibleRepositories(
4417
err.error_args[0].decode('utf-8'), err.error_args[1].decode('utf-8'), err.error_args[2].decode('utf-8')))
4418
no_context_error_translators.register(b'LockContention',
4419
lambda err: errors.LockContention('(remote lock)'))
4420
no_context_error_translators.register(b'LockFailed',
4421
lambda err: errors.LockFailed(err.error_args[0].decode('utf-8'), err.error_args[1].decode('utf-8')))
4422
no_context_error_translators.register(b'TipChangeRejected',
4423
lambda err: errors.TipChangeRejected(err.error_args[0].decode('utf8')))
4424
no_context_error_translators.register(b'UnstackableBranchFormat',
4425
lambda err: branch.UnstackableBranchFormat(*err.error_args))
4426
no_context_error_translators.register(b'UnstackableRepositoryFormat',
4427
lambda err: errors.UnstackableRepositoryFormat(*err.error_args))
4428
no_context_error_translators.register(b'FileExists',
4429
lambda err: errors.FileExists(err.error_args[0].decode('utf-8')))
4430
no_context_error_translators.register(b'DirectoryNotEmpty',
4431
lambda err: errors.DirectoryNotEmpty(err.error_args[0].decode('utf-8')))
4432
no_context_error_translators.register(b'UnknownFormat',
4433
lambda err: errors.UnknownFormatError(
4434
err.error_args[0].decode('ascii'), err.error_args[0].decode('ascii')))
4435
no_context_error_translators.register(b'InvalidURL',
4436
lambda err: urlutils.InvalidURL(
4437
err.error_args[0].decode('utf-8'), err.error_args[1].decode('ascii')))
4440
def _translate_short_readv_error(err):
4441
args = err.error_args
4442
return errors.ShortReadvError(
4443
args[0].decode('utf-8'),
4444
int(args[1].decode('ascii')), int(args[2].decode('ascii')),
4445
int(args[3].decode('ascii')))
4448
no_context_error_translators.register(b'ShortReadvError',
4449
_translate_short_readv_error)
4452
def _translate_unicode_error(err):
4453
encoding = err.error_args[0].decode('ascii')
4454
val = err.error_args[1].decode('utf-8')
4455
start = int(err.error_args[2].decode('ascii'))
4456
end = int(err.error_args[3].decode('ascii'))
4457
reason = err.error_args[4].decode('utf-8')
4458
if val.startswith('u:'):
4459
val = val[2:].decode('utf-8')
4460
elif val.startswith('s:'):
4461
val = val[2:].decode('base64')
4462
if err.error_verb == 'UnicodeDecodeError':
4463
raise UnicodeDecodeError(encoding, val, start, end, reason)
4464
elif err.error_verb == 'UnicodeEncodeError':
4465
raise UnicodeEncodeError(encoding, val, start, end, reason)
4468
no_context_error_translators.register(b'UnicodeEncodeError',
4469
_translate_unicode_error)
4470
no_context_error_translators.register(b'UnicodeDecodeError',
4471
_translate_unicode_error)
4472
no_context_error_translators.register(b'ReadOnlyError',
4473
lambda err: errors.TransportNotPossible('readonly transport'))
4474
no_context_error_translators.register(b'MemoryError',
4475
lambda err: errors.BzrError("remote server out of memory\n"
4476
"Retry non-remotely, or contact the server admin for details."))
4477
no_context_error_translators.register(b'RevisionNotPresent',
4478
lambda err: errors.RevisionNotPresent(err.error_args[0].decode('utf-8'), err.error_args[1].decode('utf-8')))
4480
no_context_error_translators.register(b'BzrCheckError',
4481
lambda err: errors.BzrCheckError(msg=err.error_args[0].decode('utf-8')))