104
73
return self._client.call_with_body_bytes_expecting_body(
105
74
method, args, body_bytes)
106
except errors.ErrorFromSmartServer as err:
75
except errors.ErrorFromSmartServer, err:
107
76
self._translate_error(err, **err_context)
110
79
def response_tuple_to_repo_format(response):
111
80
"""Convert a response tuple describing a repository format to a format."""
112
81
format = RemoteRepositoryFormat()
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')
82
format._rich_root_data = (response[0] == 'yes')
83
format._supports_tree_reference = (response[1] == 'yes')
84
format._supports_external_lookups = (response[2] == 'yes')
116
85
format._network_name = response[3]
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):
89
# Note: RemoteBzrDirFormat is in bzrdir.py
91
class RemoteBzrDir(BzrDir, _RpcHelper):
427
92
"""Control directory on a remote server, accessed via bzr:// or similar."""
429
94
def __init__(self, transport, format, _client=None, _force_probe=False):
563
186
medium = self._client._medium
564
187
if medium._is_remote_before((1, 13)):
565
188
return self._vfs_cloning_metadir(require_stacking=require_stacking)
566
verb = b'BzrDir.cloning_metadir'
189
verb = 'BzrDir.cloning_metadir'
567
190
if require_stacking:
571
194
path = self._path_for_remote_call(self._client)
573
196
response = self._call(verb, path, stacking)
574
197
except errors.UnknownSmartMethod:
575
198
medium._remember_remote_is_before((1, 13))
576
199
return self._vfs_cloning_metadir(require_stacking=require_stacking)
577
except errors.UnknownErrorFromSmartServer as err:
578
if err.error_tuple != (b'BranchReference',):
200
except errors.UnknownErrorFromSmartServer, err:
201
if err.error_tuple != ('BranchReference',):
580
203
# We need to resolve the branch reference to determine the
581
204
# cloning_metadir. This causes unnecessary RPCs to open the
582
205
# referenced branch (and bzrdir, etc) but only when the caller
583
206
# didn't already resolve the branch reference.
584
207
referenced_branch = self.open_branch()
585
return referenced_branch.controldir.cloning_metadir()
208
return referenced_branch.bzrdir.cloning_metadir()
586
209
if len(response) != 3:
587
210
raise errors.UnexpectedSmartServerResponse(response)
588
211
control_name, repo_name, branch_info = response
589
212
if len(branch_info) != 2:
590
213
raise errors.UnexpectedSmartServerResponse(response)
591
214
branch_ref, branch_name = branch_info
593
format = controldir.network_format_registry.get(control_name)
595
raise errors.UnknownFormatError(
596
kind='control', format=control_name)
215
format = bzrdir.network_format_registry.get(control_name)
600
format.repository_format = _mod_repository.network_format_registry.get(
603
raise errors.UnknownFormatError(kind='repository',
605
if branch_ref == b'ref':
217
format.repository_format = repository.network_format_registry.get(
219
if branch_ref == 'ref':
606
220
# XXX: we need possible_transports here to avoid reopening the
607
221
# connection to the referenced location
608
ref_bzrdir = _mod_bzrdir.BzrDir.open(branch_name)
222
ref_bzrdir = BzrDir.open(branch_name)
609
223
branch_format = ref_bzrdir.cloning_metadir().get_branch_format()
610
224
format.set_branch_format(branch_format)
611
elif branch_ref == b'branch':
225
elif branch_ref == 'branch':
614
branch_format = branch.network_format_registry.get(
617
raise errors.UnknownFormatError(kind='branch',
619
format.set_branch_format(branch_format)
227
format.set_branch_format(
228
branch.network_format_registry.get(branch_name))
621
230
raise errors.UnexpectedSmartServerResponse(response)
674
266
def destroy_branch(self, name=None):
675
267
"""See BzrDir.destroy_branch"""
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
269
self._real_bzrdir.destroy_branch(name=name)
692
270
self._next_open_branch_result = None
693
if response[0] != b'ok':
694
raise SmartProtocolError(
695
'unexpected response code %s' % (response,))
697
def create_workingtree(self, revision_id=None, from_branch=None,
698
accelerator_tree=None, hardlink=False):
272
def create_workingtree(self, revision_id=None, from_branch=None):
699
273
raise errors.NotLocalUrl(self.transport.base)
701
def find_branch_format(self, name=None):
275
def find_branch_format(self):
702
276
"""Find the branch 'format' for this bzrdir.
704
278
This might be a synthetic object for e.g. RemoteBranch and SVN.
706
b = self.open_branch(name=name)
280
b = self.open_branch()
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):
283
def get_branch_reference(self):
738
284
"""See BzrDir.get_branch_reference()."""
740
name = self._get_selected_branch()
742
raise errors.NoColocatedBranchSupport(self)
743
285
response = self._get_branch_reference()
744
286
if response[0] == 'ref':
745
return response[1].decode('utf-8')
749
291
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
756
292
path = self._path_for_remote_call(self._client)
757
293
medium = self._client._medium
758
294
candidate_calls = [
759
(b'BzrDir.open_branchV3', (2, 1)),
760
(b'BzrDir.open_branchV2', (1, 13)),
761
(b'BzrDir.open_branch', None),
295
('BzrDir.open_branchV3', (2, 1)),
296
('BzrDir.open_branchV2', (1, 13)),
297
('BzrDir.open_branch', None),
763
299
for verb, required_version in candidate_calls:
764
300
if required_version and medium._is_remote_before(required_version):
771
307
medium._remember_remote_is_before(required_version)
774
if verb == b'BzrDir.open_branch':
775
if response[0] != b'ok':
310
if verb == 'BzrDir.open_branch':
311
if response[0] != 'ok':
776
312
raise errors.UnexpectedSmartServerResponse(response)
777
if response[1] != b'':
313
if response[1] != '':
778
314
return ('ref', response[1])
780
return ('branch', b'')
781
if response[0] not in (b'ref', b'branch'):
316
return ('branch', '')
317
if response[0] not in ('ref', 'branch'):
782
318
raise errors.UnexpectedSmartServerResponse(response)
783
return (response[0].decode('ascii'), response[1])
785
def _get_tree_branch(self, name=None):
321
def _get_tree_branch(self):
786
322
"""See BzrDir._get_tree_branch()."""
787
return None, self.open_branch(name=name)
323
return None, self.open_branch()
789
def _open_branch(self, name, kind, location_or_format,
790
ignore_fallbacks=False, possible_transports=None):
325
def open_branch(self, name=None, unsupported=False,
326
ignore_fallbacks=False):
328
raise NotImplementedError('unsupported flag support not implemented yet.')
329
if self._next_open_branch_result is not None:
330
# See create_branch for details.
331
result = self._next_open_branch_result
332
self._next_open_branch_result = None
334
response = self._get_branch_reference()
335
if response[0] == 'ref':
792
336
# a branch reference, use the existing BranchReference logic.
793
337
format = BranchReferenceFormat()
794
338
return format.open(self, name=name, _found=True,
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
339
location=response[1], ignore_fallbacks=ignore_fallbacks)
340
branch_format_name = response[1]
799
341
if not branch_format_name:
800
342
branch_format_name = None
801
343
format = RemoteBranchFormat(network_name=branch_format_name)
802
344
return RemoteBranch(self, self.find_repository(), format=format,
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)
345
setup_stacking=not ignore_fallbacks, name=name)
825
347
def _open_repo_v1(self, path):
826
verb = b'BzrDir.find_repository'
348
verb = 'BzrDir.find_repository'
827
349
response = self._call(verb, path)
828
if response[0] != b'ok':
350
if response[0] != 'ok':
829
351
raise errors.UnexpectedSmartServerResponse(response)
830
352
# servers that only support the v1 method don't support external
831
353
# references either.
832
354
self._ensure_real()
833
355
repo = self._real_bzrdir.open_repository()
834
response = response + (b'no', repo._format.network_name())
356
response = response + ('no', repo._format.network_name())
835
357
return response, repo
837
359
def _open_repo_v2(self, path):
838
verb = b'BzrDir.find_repositoryV2'
360
verb = 'BzrDir.find_repositoryV2'
839
361
response = self._call(verb, path)
840
if response[0] != b'ok':
362
if response[0] != 'ok':
841
363
raise errors.UnexpectedSmartServerResponse(response)
842
364
self._ensure_real()
843
365
repo = self._real_bzrdir.open_repository()
936
441
"""Upgrading of remote bzrdirs is not supported yet."""
939
def needs_format_conversion(self, format):
444
def needs_format_conversion(self, format=None):
940
445
"""Upgrading of remote bzrdirs is not supported yet."""
447
symbol_versioning.warn(symbol_versioning.deprecated_in((1, 13, 0))
448
% 'needs_format_conversion(format=None)')
451
def clone(self, url, revision_id=None, force_new_repo=False,
452
preserve_stacking=False):
454
return self._real_bzrdir.clone(url, revision_id=revision_id,
455
force_new_repo=force_new_repo, preserve_stacking=preserve_stacking)
943
457
def _get_config(self):
944
458
return RemoteBzrDirConfig(self)
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,
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 default_revision: For lines that don't match a basis, mark them
972
with this revision id. Not all implementations will make use of
975
ret = self._repository._annotate_file_revision(
976
self.get_revision_id(), path, file_id=None,
977
default_revision=default_revision)
979
return super(RemoteInventoryTree, self).annotate_iter(
980
path, default_revision=default_revision)
984
class RemoteRepositoryFormat(vf_repository.VersionedFileRepositoryFormat):
461
class RemoteRepositoryFormat(repository.RepositoryFormat):
985
462
"""Format for repositories accessed over a _SmartClient.
987
464
Instances of this repository are represented by RemoteRepository
1074
529
self._custom_format.supports_tree_reference
1075
530
return self._supports_tree_reference
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):
532
def _vfs_initialize(self, a_bzrdir, shared):
1086
533
"""Helper for common code in initialize."""
1087
534
if self._custom_format:
1088
535
# Custom format requested
1089
result = self._custom_format.initialize(
1090
a_controldir, shared=shared)
536
result = self._custom_format.initialize(a_bzrdir, shared=shared)
1091
537
elif self._creating_bzrdir is not None:
1092
538
# Use the format that the repository we were created to back
1094
540
prior_repo = self._creating_bzrdir.open_repository()
1095
541
prior_repo._ensure_real()
1096
542
result = prior_repo._real_repository._format.initialize(
1097
a_controldir, shared=shared)
543
a_bzrdir, shared=shared)
1099
545
# assume that a_bzr is a RemoteBzrDir but the smart server didn't
1100
546
# support remote initialization.
1101
547
# We delegate to a real object at this point (as RemoteBzrDir
1102
548
# delegate to the repository format which would lead to infinite
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)
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)
1106
552
if not isinstance(result, RemoteRepository):
1107
return self.open(a_controldir)
553
return self.open(a_bzrdir)
1111
def initialize(self, a_controldir, shared=False):
557
def initialize(self, a_bzrdir, shared=False):
1112
558
# Being asked to create on a non RemoteBzrDir:
1113
if not isinstance(a_controldir, RemoteBzrDir):
1114
return self._vfs_initialize(a_controldir, shared)
1115
medium = a_controldir._client._medium
559
if not isinstance(a_bzrdir, RemoteBzrDir):
560
return self._vfs_initialize(a_bzrdir, shared)
561
medium = a_bzrdir._client._medium
1116
562
if medium._is_remote_before((1, 13)):
1117
return self._vfs_initialize(a_controldir, shared)
563
return self._vfs_initialize(a_bzrdir, shared)
1118
564
# Creating on a remote bzr dir.
1119
565
# 1) get the network name to use.
1120
566
if self._custom_format:
1122
568
elif self._network_name:
1123
569
network_name = self._network_name
1125
# Select the current breezy default and ask for that.
1126
reference_bzrdir_format = controldir.format_registry.get(
571
# Select the current bzrlib default and ask for that.
572
reference_bzrdir_format = bzrdir.format_registry.get('default')()
1128
573
reference_format = reference_bzrdir_format.repository_format
1129
574
network_name = reference_format.network_name()
1130
575
# 2) try direct creation via RPC
1131
path = a_controldir._path_for_remote_call(a_controldir._client)
1132
verb = b'BzrDir.create_repository'
576
path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
577
verb = 'BzrDir.create_repository'
1134
shared_str = b'True'
1136
shared_str = b'False'
1138
response = a_controldir._call(verb, path, network_name, shared_str)
583
response = a_bzrdir._call(verb, path, network_name, shared_str)
1139
584
except errors.UnknownSmartMethod:
1140
585
# Fallback - use vfs methods
1141
586
medium._remember_remote_is_before((1, 13))
1142
return self._vfs_initialize(a_controldir, shared)
587
return self._vfs_initialize(a_bzrdir, shared)
1144
589
# Turn the response into a RemoteRepository object.
1145
590
format = response_tuple_to_repo_format(response[1:])
1146
591
# Used to support creating a real format instance when needed.
1147
format._creating_bzrdir = a_controldir
1148
remote_repo = RemoteRepository(a_controldir, format)
592
format._creating_bzrdir = a_bzrdir
593
remote_repo = RemoteRepository(a_bzrdir, format)
1149
594
format._creating_repo = remote_repo
1150
595
return remote_repo
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()
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()
1157
602
def _ensure_real(self):
1158
603
if self._custom_format is None:
1160
self._custom_format = _mod_repository.network_format_registry.get(
1163
raise errors.UnknownFormatError(kind='repository',
1164
format=self._network_name)
604
self._custom_format = repository.network_format_registry.get(
1167
608
def _fetch_order(self):
1517
909
# TODO: Move to RepositoryBase and unify with the regular Repository
1518
910
# one; unfortunately the tests rely on slightly different behaviour at
1519
911
# present -- mbp 20090710
1520
return (self.__class__ is other.__class__
1521
and self.controldir.transport.base == other.controldir.transport.base)
912
return (self.__class__ is other.__class__ and
913
self.bzrdir.transport.base == other.bzrdir.transport.base)
1523
915
def get_graph(self, other_repository=None):
1524
916
"""Return the graph for this repository format"""
1525
917
parents_provider = self._make_parents_provider(other_repository)
1526
918
return graph.Graph(parents_provider)
1528
921
def get_known_graph_ancestry(self, revision_ids):
1529
922
"""Return the known graph for a set of revision ids and their ancestors.
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)
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)
1537
929
def gather_stats(self, revid=None, committers=None):
1538
930
"""See Repository.gather_stats()."""
1539
path = self.controldir._path_for_remote_call(self._client)
931
path = self.bzrdir._path_for_remote_call(self._client)
1540
932
# revid can be None to indicate no revisions, not just NULL_REVISION
1541
if revid is None or _mod_revision.is_null(revid):
933
if revid is None or revision.is_null(revid):
1544
936
fmt_revid = revid
1545
937
if committers is None or not committers:
1546
fmt_committers = b'no'
938
fmt_committers = 'no'
1548
fmt_committers = b'yes'
940
fmt_committers = 'yes'
1549
941
response_tuple, response_handler = self._call_expecting_body(
1550
b'Repository.gather_stats', path, fmt_revid, fmt_committers)
1551
if response_tuple[0] != b'ok':
942
'Repository.gather_stats', path, fmt_revid, fmt_committers)
943
if response_tuple[0] != 'ok':
1552
944
raise errors.UnexpectedSmartServerResponse(response_tuple)
1554
946
body = response_handler.read_body_bytes()
1556
for line in body.split(b'\n'):
948
for line in body.split('\n'):
1559
key, val_text = line.split(b':')
1560
key = key.decode('ascii')
951
key, val_text = line.split(':')
1561
952
if key in ('revisions', 'size', 'committers'):
1562
953
result[key] = int(val_text)
1563
954
elif key in ('firstrev', 'latestrev'):
1564
values = val_text.split(b' ')[1:]
1565
result[key] = (float(values[0]), int(values[1]))
955
values = val_text.split(' ')[1:]
956
result[key] = (float(values[0]), long(values[1]))
1846
1195
raise errors.UnexpectedSmartServerResponse(response)
1848
1197
def sprout(self, to_bzrdir, revision_id=None):
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()
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)
1870
1203
return dest_repo
1872
# These methods are just thin shims to the VFS object for now.
1205
### These methods are just thin shims to the VFS object for now.
1874
1207
def revision_tree(self, 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]
1209
return self._real_repository.revision_tree(revision_id)
1883
1211
def get_serializer_format(self):
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)
1213
return self._real_repository.get_serializer_format()
1895
1215
def get_commit_builder(self, branch, parents, config, timestamp=None,
1896
1216
timezone=None, committer=None, revprops=None,
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()
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)
1922
1227
def add_fallback_repository(self, repository):
1923
1228
"""Add a repository to use for looking up data not held locally.
1962
1266
return self._real_repository.add_inventory(revid, inv, parents)
1964
1268
def add_inventory_by_delta(self, basis_revision_id, delta, new_revision_id,
1965
parents, basis_inv=None, propagate_caches=False):
1269
parents, basis_inv=None, propagate_caches=False):
1966
1270
self._ensure_real()
1967
1271
return self._real_repository.add_inventory_by_delta(basis_revision_id,
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)
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)
1997
1281
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):
2045
1282
self._ensure_real()
2046
return self._real_repository._iter_inventories(revision_ids, ordering)
1283
return self._real_repository.get_inventory(revision_id)
2048
1285
def iter_inventories(self, revision_ids, ordering=None):
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()
1287
return self._real_repository.iter_inventories(revision_ids, ordering)
2124
1290
def get_revision(self, revision_id):
2125
with self.lock_read():
2126
return self.get_revisions([revision_id])[0]
1292
return self._real_repository.get_revision(revision_id)
2128
1294
def get_transaction(self):
2129
1295
self._ensure_real()
2130
1296
return self._real_repository.get_transaction()
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)
1299
def clone(self, a_bzrdir, revision_id=None):
1301
return self._real_repository.clone(a_bzrdir, revision_id=revision_id)
2139
1303
def make_working_trees(self):
2140
1304
"""See 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'
1306
return self._real_repository.make_working_trees()
2152
1308
def refresh_data(self):
2153
"""Re-read any data needed to synchronise with disk.
1309
"""Re-read any data needed to to synchronise with disk.
2155
1311
This method is intended to be called after another repository instance
2156
1312
(such as one used by a smart server) has inserted data into the
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.
1313
repository. It may not be called during a write group, but may be
1314
called at any other time.
1316
if self.is_in_write_group():
1317
raise errors.InternalBzrError(
1318
"May not refresh_data while in a write group.")
2163
1319
if self._real_repository is not None:
2164
1320
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()
2169
1322
def revision_ids_to_search_result(self, result_set):
2170
1323
"""Convert a set of revision ids to a graph SearchResult."""
2171
1324
result_parents = set()
2172
for parents in viewvalues(self.get_graph().get_parent_map(result_set)):
1325
for parents in self.get_graph().get_parent_map(
1326
result_set).itervalues():
2173
1327
result_parents.update(parents)
2174
1328
included_keys = result_set.intersection(result_parents)
2175
1329
start_keys = result_set.difference(included_keys)
2176
1330
exclude_keys = result_parents.difference(result_set)
2177
result = vf_search.SearchResult(start_keys, exclude_keys,
2178
len(result_set), result_set)
1331
result = graph.SearchResult(start_keys, exclude_keys,
1332
len(result_set), result_set)
2181
def search_missing_revision_ids(self, other,
2182
find_ghosts=True, revision_ids=None, if_present_ids=None,
1336
def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
2184
1337
"""Return the revision ids that other has that this does not.
2186
1339
These are returned in topological order.
2188
1341
revision_id: only return revision ids included by revision_id.
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)
1343
return repository.InterRepository.get(
1344
other, self).search_missing_revision_ids(revision_id, find_ghosts)
2196
def fetch(self, source, revision_id=None, find_ghosts=False,
1346
def fetch(self, source, revision_id=None, pb=None, find_ghosts=False,
2198
1348
# No base implementation to use as RemoteRepository is not a subclass
2199
1349
# of Repository; so this is a copy of Repository.fetch().
2200
1350
if fetch_spec is not None and revision_id is not None:
2237
1388
return self._real_repository._get_versioned_file_checker(
2238
1389
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)
2291
1391
def iter_files_bytes(self, desired_files):
2292
1392
"""See Repository.iter_file_bytes.
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)
1395
return self._real_repository.iter_files_bytes(desired_files)
2324
1397
def get_parent_map(self, revision_ids):
2325
"""See breezy.Graph.get_parent_map()."""
1398
"""See bzrlib.Graph.get_parent_map()."""
2326
1399
return self._make_parents_provider().get_parent_map(revision_ids)
2328
1401
def _get_parent_map_rpc(self, keys):
2445
1529
revision_graph[d[0]] = (NULL_REVISION,)
2446
1530
return revision_graph
2448
1533
def get_signature_text(self, 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()
1535
return self._real_repository.get_signature_text(revision_id)
2469
1538
def _get_inventory_xml(self, 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)
1540
return self._real_repository._get_inventory_xml(revision_id)
2476
1542
def reconcile(self, other=None, thorough=False):
2477
from ..reconcile import ReconcileResult
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 = ReconcileResult()
2490
result.garbage_inventories = None
2491
result.inconsistent_parents = None
2492
result.aborted = None
2493
for line in body.split(b'\n'):
2496
key, val_text = line.split(b':')
2497
if key == b"garbage_inventories":
2498
result.garbage_inventories = int(val_text)
2499
elif key == b"inconsistent_parents":
2500
result.inconsistent_parents = int(val_text)
2502
mutter("unknown reconcile key %r" % key)
1544
return self._real_repository.reconcile(other=other, thorough=thorough)
2505
1546
def all_revision_ids(self):
2506
path = self.controldir._path_for_remote_call(self._client)
2508
response_tuple, response_handler = self._call_expecting_body(
2509
b"Repository.all_revision_ids", path)
2510
except errors.UnknownSmartMethod:
2512
return self._real_repository.all_revision_ids()
2513
if response_tuple != (b"ok", ):
2514
raise errors.UnexpectedSmartServerResponse(response_tuple)
2515
revids = set(response_handler.read_body_bytes().splitlines())
2516
for fallback in self._fallback_repositories:
2517
revids.update(set(fallback.all_revision_ids()))
2520
def _filtered_revision_trees(self, revision_ids, file_ids):
2521
"""Return Tree for a revision on this branch with only some files.
2523
:param revision_ids: a sequence of revision-ids;
2524
a revision-id may not be None or b'null:'
2525
:param file_ids: if not None, the result is filtered
2526
so that only those file-ids, their parents and their
2527
children are included.
2529
inventories = self.iter_inventories(revision_ids)
2530
for inv in inventories:
2531
# Should we introduce a FilteredRevisionTree class rather
2532
# than pre-filter the inventory here?
2533
filtered_inv = inv.filter(file_ids)
2534
yield InventoryRevisionTree(self, filtered_inv, filtered_inv.revision_id)
1548
return self._real_repository.all_revision_ids()
2536
1551
def get_deltas_for_revisions(self, revisions, specific_fileids=None):
2537
with self.lock_read():
2538
medium = self._client._medium
2539
if medium._is_remote_before((1, 2)):
2541
for delta in self._real_repository.get_deltas_for_revisions(
2542
revisions, specific_fileids):
2545
# Get the revision-ids of interest
2546
required_trees = set()
2547
for revision in revisions:
2548
required_trees.add(revision.revision_id)
2549
required_trees.update(revision.parent_ids[:1])
2551
# Get the matching filtered trees. Note that it's more
2552
# efficient to pass filtered trees to changes_from() rather
2553
# than doing the filtering afterwards. changes_from() could
2554
# arguably do the filtering itself but it's path-based, not
2555
# file-id based, so filtering before or afterwards is
2557
if specific_fileids is None:
2558
trees = dict((t.get_revision_id(), t) for
2559
t in self.revision_trees(required_trees))
2561
trees = dict((t.get_revision_id(), t) for
2562
t in self._filtered_revision_trees(required_trees,
2565
# Calculate the deltas
2566
for revision in revisions:
2567
if not revision.parent_ids:
2568
old_tree = self.revision_tree(_mod_revision.NULL_REVISION)
2570
old_tree = trees[revision.parent_ids[0]]
2571
yield trees[revision.revision_id].changes_from(old_tree)
1553
return self._real_repository.get_deltas_for_revisions(revisions,
1554
specific_fileids=specific_fileids)
2573
1557
def get_revision_delta(self, revision_id, specific_fileids=None):
2574
with self.lock_read():
2575
r = self.get_revision(revision_id)
2576
return list(self.get_deltas_for_revisions([r],
2577
specific_fileids=specific_fileids))[0]
1559
return self._real_repository.get_revision_delta(revision_id,
1560
specific_fileids=specific_fileids)
2579
1563
def revision_trees(self, revision_ids):
2580
with self.lock_read():
2581
inventories = self.iter_inventories(revision_ids)
2582
for inv in inventories:
2583
yield RemoteInventoryTree(self, inv, inv.revision_id)
1565
return self._real_repository.revision_trees(revision_ids)
2585
1568
def get_revision_reconcile(self, revision_id):
2586
with self.lock_read():
2588
return self._real_repository.get_revision_reconcile(revision_id)
1570
return self._real_repository.get_revision_reconcile(revision_id)
2590
1573
def check(self, revision_ids=None, callback_refs=None, check_repo=True):
2591
with self.lock_read():
2593
return self._real_repository.check(revision_ids=revision_ids,
2594
callback_refs=callback_refs, check_repo=check_repo)
1575
return self._real_repository.check(revision_ids=revision_ids,
1576
callback_refs=callback_refs, check_repo=check_repo)
2596
1578
def copy_content_into(self, destination, revision_id=None):
2597
"""Make a complete copy of the content in self into destination.
2599
This is a destructive operation! Do not use it on existing
2602
interrepo = _mod_repository.InterRepository.get(self, destination)
2603
return interrepo.copy_content(revision_id)
1580
return self._real_repository.copy_content_into(
1581
destination, revision_id=revision_id)
2605
1583
def _copy_repository_tarball(self, to_bzrdir, revision_id=None):
2606
1584
# get a tarball of the remote repository, and copy from that into the
1586
from bzrlib import osutils
2609
1588
# TODO: Maybe a progress bar while streaming the tarball?
2610
note(gettext("Copying repository content as tarball..."))
1589
note("Copying repository content as tarball...")
2611
1590
tar_file = self._get_tarball('bz2')
2612
1591
if tar_file is None:
2614
1593
destination = to_bzrdir.create_repository()
2616
1595
tar = tarfile.open('repository', fileobj=tar_file,
2618
1597
tmpdir = osutils.mkdtemp()
2620
tar.extractall(tmpdir)
2621
tmp_bzrdir = _mod_bzrdir.BzrDir.open(tmpdir)
1599
_extract_tar(tar, tmpdir)
1600
tmp_bzrdir = BzrDir.open(tmpdir)
2622
1601
tmp_repo = tmp_bzrdir.open_repository()
2623
1602
tmp_repo.copy_content_into(destination, revision_id)
2712
1680
self._ensure_real()
2713
1681
return self._real_repository.texts
2715
def _iter_revisions_rpc(self, revision_ids):
2716
body = b"\n".join(revision_ids)
2717
path = self.controldir._path_for_remote_call(self._client)
2718
response_tuple, response_handler = (
2719
self._call_with_body_bytes_expecting_body(
2720
b"Repository.iter_revisions", (path, ), body))
2721
if response_tuple[0] != b"ok":
2722
raise errors.UnexpectedSmartServerResponse(response_tuple)
2723
serializer_format = response_tuple[1].decode('ascii')
2724
serializer = serializer_format_registry.get(serializer_format)
2725
byte_stream = response_handler.read_streamed_body()
2726
decompressor = zlib.decompressobj()
2728
for bytes in byte_stream:
2729
chunks.append(decompressor.decompress(bytes))
2730
if decompressor.unused_data != b"":
2731
chunks.append(decompressor.flush())
2732
yield serializer.read_revision_from_string(b"".join(chunks))
2733
unused = decompressor.unused_data
2734
decompressor = zlib.decompressobj()
2735
chunks = [decompressor.decompress(unused)]
2736
chunks.append(decompressor.flush())
2737
text = b"".join(chunks)
2739
yield serializer.read_revision_from_string(b"".join(chunks))
2741
def iter_revisions(self, revision_ids):
2742
for rev_id in revision_ids:
2743
if not rev_id or not isinstance(rev_id, bytes):
2744
raise errors.InvalidRevisionId(
2745
revision_id=rev_id, branch=self)
2746
with self.lock_read():
2748
missing = set(revision_ids)
2749
for rev in self._iter_revisions_rpc(revision_ids):
2750
missing.remove(rev.revision_id)
2751
yield (rev.revision_id, rev)
2752
for fallback in self._fallback_repositories:
2755
for (revid, rev) in fallback.iter_revisions(missing):
2758
missing.remove(revid)
2759
for revid in missing:
2761
except errors.UnknownSmartMethod:
2763
for entry in self._real_repository.iter_revisions(revision_ids):
1684
def get_revisions(self, revision_ids):
1686
return self._real_repository.get_revisions(revision_ids)
2766
1688
def supports_rich_root(self):
2767
1689
return self._format.rich_root_data
1691
def iter_reverse_revision_history(self, revision_id):
1693
return self._real_repository.iter_reverse_revision_history(revision_id)
2770
1696
def _serializer(self):
2771
1697
return self._format._serializer
2773
1699
def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
2774
with self.lock_write():
2775
signature = gpg_strategy.sign(plaintext, gpg.MODE_CLEAR)
2776
self.add_signature_text(revision_id, signature)
1701
return self._real_repository.store_revision_signature(
1702
gpg_strategy, plaintext, revision_id)
2778
1704
def add_signature_text(self, revision_id, signature):
2779
if self._real_repository:
2780
# If there is a real repository the write group will
2781
# be in the real repository as well, so use that:
2783
return self._real_repository.add_signature_text(
2784
revision_id, signature)
2785
path = self.controldir._path_for_remote_call(self._client)
2786
response, handler = self._call_with_body_bytes_expecting_body(
2787
b'Repository.add_signature_text', (path, self._lock_token,
2789
tuple([token.encode('utf-8')
2790
for token in self._write_group_tokens]),
2792
handler.cancel_read_body()
2794
if response[0] != b'ok':
2795
raise errors.UnexpectedSmartServerResponse(response)
2796
self._write_group_tokens = [token.decode(
2797
'utf-8') for token in response[1:]]
1706
return self._real_repository.add_signature_text(revision_id, signature)
2799
1708
def has_signature_for_revision_id(self, revision_id):
2800
path = self.controldir._path_for_remote_call(self._client)
2802
response = self._call(b'Repository.has_signature_for_revision_id',
2804
except errors.UnknownSmartMethod:
2806
return self._real_repository.has_signature_for_revision_id(
2808
if response[0] not in (b'yes', b'no'):
2809
raise SmartProtocolError(
2810
'unexpected response code %s' % (response,))
2811
if response[0] == b'yes':
2813
for fallback in self._fallback_repositories:
2814
if fallback.has_signature_for_revision_id(revision_id):
2818
def verify_revision_signature(self, revision_id, gpg_strategy):
2819
with self.lock_read():
2820
if not self.has_signature_for_revision_id(revision_id):
2821
return gpg.SIGNATURE_NOT_SIGNED, None
2822
signature = self.get_signature_text(revision_id)
2824
testament = _mod_testament.Testament.from_revision(
2827
(status, key, signed_plaintext) = gpg_strategy.verify(signature)
2828
if testament.as_short_text() != signed_plaintext:
2829
return gpg.SIGNATURE_NOT_VALID, None
2830
return (status, key)
1710
return self._real_repository.has_signature_for_revision_id(revision_id)
2832
1712
def item_keys_introduced_by(self, revision_ids, _files_pb=None):
2833
1713
self._ensure_real()
2834
1714
return self._real_repository.item_keys_introduced_by(revision_ids,
2835
_files_pb=_files_pb)
1715
_files_pb=_files_pb)
1717
def revision_graph_can_have_wrong_parents(self):
1718
# The answer depends on the remote repo format.
1720
return self._real_repository.revision_graph_can_have_wrong_parents()
2837
1722
def _find_inconsistent_revision_parents(self, revisions_iterator=None):
2838
1723
self._ensure_real()
2856
1742
:param recipe: A search recipe (start, stop, count).
2857
1743
:return: Serialised bytes.
2859
start_keys = b' '.join(recipe[1])
2860
stop_keys = b' '.join(recipe[2])
2861
count = str(recipe[3]).encode('ascii')
2862
return b'\n'.join((start_keys, stop_keys, count))
1745
start_keys = ' '.join(recipe[1])
1746
stop_keys = ' '.join(recipe[2])
1747
count = str(recipe[3])
1748
return '\n'.join((start_keys, stop_keys, count))
2864
1750
def _serialise_search_result(self, search_result):
2865
parts = search_result.get_network_struct()
2866
return b'\n'.join(parts)
1751
if isinstance(search_result, graph.PendingAncestryResult):
1752
parts = ['ancestry-of']
1753
parts.extend(search_result.heads)
1755
recipe = search_result.get_recipe()
1756
parts = [recipe[0], self._serialise_search_recipe(recipe)]
1757
return '\n'.join(parts)
2868
1759
def autopack(self):
2869
path = self.controldir._path_for_remote_call(self._client)
1760
path = self.bzrdir._path_for_remote_call(self._client)
2871
response = self._call(b'PackRepository.autopack', path)
1762
response = self._call('PackRepository.autopack', path)
2872
1763
except errors.UnknownSmartMethod:
2873
1764
self._ensure_real()
2874
1765
self._real_repository._pack_collection.autopack()
2876
1767
self.refresh_data()
2877
if response[0] != b'ok':
2878
raise errors.UnexpectedSmartServerResponse(response)
2880
def _revision_archive(self, revision_id, format, name, root, subdir,
2882
path = self.controldir._path_for_remote_call(self._client)
2883
format = format or ''
2885
subdir = subdir or ''
2886
force_mtime = int(force_mtime) if force_mtime is not None else None
2888
response, protocol = self._call_expecting_body(
2889
b'Repository.revision_archive', path,
2891
format.encode('ascii'),
2892
os.path.basename(name).encode('utf-8'),
2893
root.encode('utf-8'),
2894
subdir.encode('utf-8'),
2896
except errors.UnknownSmartMethod:
2898
if response[0] == b'ok':
2899
return iter([protocol.read_body_bytes()])
2900
raise errors.UnexpectedSmartServerResponse(response)
2902
def _annotate_file_revision(self, revid, tree_path, file_id, default_revision):
2903
path = self.controldir._path_for_remote_call(self._client)
2904
tree_path = tree_path.encode('utf-8')
2905
file_id = file_id or b''
2906
default_revision = default_revision or b''
2908
response, handler = self._call_expecting_body(
2909
b'Repository.annotate_file_revision', path,
2910
revid, tree_path, file_id, default_revision)
2911
except errors.UnknownSmartMethod:
2913
if response[0] != b'ok':
2914
raise errors.UnexpectedSmartServerResponse(response)
2915
return map(tuple, bencode.bdecode(handler.read_body_bytes()))
2918
class RemoteStreamSink(vf_repository.StreamSink):
1768
if response[0] != 'ok':
1769
raise errors.UnexpectedSmartServerResponse(response)
1772
class RemoteStreamSink(repository.StreamSink):
2920
1774
def _insert_real(self, stream, src_format, resume_tokens):
2921
1775
self.target_repo._ensure_real()
3265
2059
def network_name(self):
3266
2060
return self._network_name
3268
def open(self, a_controldir, name=None, ignore_fallbacks=False):
3269
return a_controldir.open_branch(name=name,
3270
ignore_fallbacks=ignore_fallbacks)
2062
def open(self, a_bzrdir, name=None, ignore_fallbacks=False):
2063
return a_bzrdir.open_branch(name=name,
2064
ignore_fallbacks=ignore_fallbacks)
3272
def _vfs_initialize(self, a_controldir, name, append_revisions_only,
2066
def _vfs_initialize(self, a_bzrdir, name):
3274
2067
# Initialisation when using a local bzrdir object, or a non-vfs init
3275
2068
# method is not available on the server.
3276
2069
# self._custom_format is always set - the start of initialize ensures
3278
if isinstance(a_controldir, RemoteBzrDir):
3279
a_controldir._ensure_real()
3280
result = self._custom_format.initialize(a_controldir._real_bzrdir,
3281
name=name, append_revisions_only=append_revisions_only,
3282
repository=repository)
2071
if isinstance(a_bzrdir, RemoteBzrDir):
2072
a_bzrdir._ensure_real()
2073
result = self._custom_format.initialize(a_bzrdir._real_bzrdir,
3284
2076
# We assume the bzrdir is parameterised; it may not be.
3285
result = self._custom_format.initialize(a_controldir, name=name,
3286
append_revisions_only=append_revisions_only,
3287
repository=repository)
3288
if (isinstance(a_controldir, RemoteBzrDir)
3289
and not isinstance(result, RemoteBranch)):
3290
result = RemoteBranch(a_controldir, a_controldir.find_repository(), result,
2077
result = self._custom_format.initialize(a_bzrdir, name)
2078
if (isinstance(a_bzrdir, RemoteBzrDir) and
2079
not isinstance(result, RemoteBranch)):
2080
result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result,
3294
def initialize(self, a_controldir, name=None, repository=None,
3295
append_revisions_only=None):
3297
name = a_controldir._get_selected_branch()
2084
def initialize(self, a_bzrdir, name=None):
3298
2085
# 1) get the network name to use.
3299
2086
if self._custom_format:
3300
2087
network_name = self._custom_format.network_name()
3302
# Select the current breezy default and ask for that.
3303
reference_bzrdir_format = controldir.format_registry.get(
2089
# Select the current bzrlib default and ask for that.
2090
reference_bzrdir_format = bzrdir.format_registry.get('default')()
3305
2091
reference_format = reference_bzrdir_format.get_branch_format()
3306
2092
self._custom_format = reference_format
3307
2093
network_name = reference_format.network_name()
3308
2094
# Being asked to create on a non RemoteBzrDir:
3309
if not isinstance(a_controldir, RemoteBzrDir):
3310
return self._vfs_initialize(a_controldir, name=name,
3311
append_revisions_only=append_revisions_only,
3312
repository=repository)
3313
medium = a_controldir._client._medium
2095
if not isinstance(a_bzrdir, RemoteBzrDir):
2096
return self._vfs_initialize(a_bzrdir, name=name)
2097
medium = a_bzrdir._client._medium
3314
2098
if medium._is_remote_before((1, 13)):
3315
return self._vfs_initialize(a_controldir, name=name,
3316
append_revisions_only=append_revisions_only,
3317
repository=repository)
2099
return self._vfs_initialize(a_bzrdir, name=name)
3318
2100
# Creating on a remote bzr dir.
3319
2101
# 2) try direct creation via RPC
3320
path = a_controldir._path_for_remote_call(a_controldir._client)
2102
path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
2103
if name is not None:
3322
2104
# XXX JRV20100304: Support creating colocated branches
3323
2105
raise errors.NoColocatedBranchSupport(self)
3324
verb = b'BzrDir.create_branch'
2106
verb = 'BzrDir.create_branch'
3326
response = a_controldir._call(verb, path, network_name)
2108
response = a_bzrdir._call(verb, path, network_name)
3327
2109
except errors.UnknownSmartMethod:
3328
2110
# Fallback - use vfs methods
3329
2111
medium._remember_remote_is_before((1, 13))
3330
return self._vfs_initialize(a_controldir, name=name,
3331
append_revisions_only=append_revisions_only,
3332
repository=repository)
3333
if response[0] != b'ok':
2112
return self._vfs_initialize(a_bzrdir, name=name)
2113
if response[0] != 'ok':
3334
2114
raise errors.UnexpectedSmartServerResponse(response)
3335
2115
# Turn the response into a RemoteRepository object.
3336
2116
format = RemoteBranchFormat(network_name=response[1])
3337
2117
repo_format = response_tuple_to_repo_format(response[3:])
3338
repo_path = response[2].decode('utf-8')
3339
if repository is not None:
3340
remote_repo_url = urlutils.join(a_controldir.user_url, repo_path)
3341
url_diff = urlutils.relative_url(repository.user_url,
3344
raise AssertionError(
3345
'repository.user_url %r does not match URL from server '
3346
'response (%r + %r)'
3347
% (repository.user_url, a_controldir.user_url, repo_path))
3348
remote_repo = repository
2118
if response[2] == '':
2119
repo_bzrdir = a_bzrdir
3351
repo_bzrdir = a_controldir
3353
repo_bzrdir = RemoteBzrDir(
3354
a_controldir.root_transport.clone(
3355
repo_path), a_controldir._format,
3356
a_controldir._client)
3357
remote_repo = RemoteRepository(repo_bzrdir, repo_format)
3358
remote_branch = RemoteBranch(a_controldir, remote_repo,
3359
format=format, setup_stacking=False, name=name)
3360
if append_revisions_only:
3361
remote_branch.set_append_revisions_only(append_revisions_only)
2121
repo_bzrdir = RemoteBzrDir(
2122
a_bzrdir.root_transport.clone(response[2]), a_bzrdir._format,
2124
remote_repo = RemoteRepository(repo_bzrdir, repo_format)
2125
remote_branch = RemoteBranch(a_bzrdir, remote_repo,
2126
format=format, setup_stacking=False, name=name)
3362
2127
# XXX: We know this is a new branch, so it must have revno 0, revid
3363
2128
# NULL_REVISION. Creating the branch locked would make this be unable
3364
2129
# to be wrong; here its simply very unlikely to be wrong. RBC 20090225
3383
2148
self._ensure_real()
3384
2149
return self._custom_format.supports_set_append_revisions_only()
3386
def _use_default_local_heads_to_fetch(self):
3387
# If the branch format is a metadir format *and* its heads_to_fetch
3388
# implementation is not overridden vs the base class, we can use the
3389
# base class logic rather than use the heads_to_fetch RPC. This is
3390
# usually cheaper in terms of net round trips, as the last-revision and
3391
# tags info fetched is cached and would be fetched anyway.
3393
if isinstance(self._custom_format, bzrbranch.BranchFormatMetadir):
3394
branch_class = self._custom_format._branch_class()
3395
heads_to_fetch_impl = get_unbound_function(
3396
branch_class.heads_to_fetch)
3397
if heads_to_fetch_impl is get_unbound_function(branch.Branch.heads_to_fetch):
3402
class RemoteBranchStore(_mod_config.IniFileStore):
3403
"""Branch store which attempts to use HPSS calls to retrieve branch store.
3405
Note that this is specific to bzr-based formats.
3408
def __init__(self, branch):
3409
super(RemoteBranchStore, self).__init__()
3410
self.branch = branch
3412
self._real_store = None
3414
def external_url(self):
3415
return urlutils.join(self.branch.user_url, 'branch.conf')
3417
def _load_content(self):
3418
path = self.branch._remote_path()
3420
response, handler = self.branch._call_expecting_body(
3421
b'Branch.get_config_file', path)
3422
except errors.UnknownSmartMethod:
3424
return self._real_store._load_content()
3425
if len(response) and response[0] != b'ok':
3426
raise errors.UnexpectedSmartServerResponse(response)
3427
return handler.read_body_bytes()
3429
def _save_content(self, content):
3430
path = self.branch._remote_path()
3432
response, handler = self.branch._call_with_body_bytes_expecting_body(
3433
b'Branch.put_config_file', (path,
3434
self.branch._lock_token, self.branch._repo_lock_token),
3436
except errors.UnknownSmartMethod:
3438
return self._real_store._save_content(content)
3439
handler.cancel_read_body()
3440
if response != (b'ok', ):
3441
raise errors.UnexpectedSmartServerResponse(response)
3443
def _ensure_real(self):
3444
self.branch._ensure_real()
3445
if self._real_store is None:
3446
self._real_store = _mod_config.BranchStore(self.branch)
3449
2152
class RemoteBranch(branch.Branch, _RpcHelper, lock._RelockDebugMixin):
3450
2153
"""Branch stored on a server accessed by HPSS RPC.
3998
2644
self._ensure_real()
3999
2645
return self._real_branch._set_parent_location(url)
4001
2648
def pull(self, source, overwrite=False, stop_revision=None,
4003
with self.lock_write():
4004
self._clear_cached_state_of_remote_branch_only()
4006
return self._real_branch.pull(
4007
source, overwrite=overwrite, stop_revision=stop_revision,
4008
_override_hook_target=self, **kwargs)
4010
def push(self, target, overwrite=False, stop_revision=None, lossy=False):
4011
with self.lock_read():
4013
return self._real_branch.push(
4014
target, overwrite=overwrite, stop_revision=stop_revision, lossy=lossy,
4015
_override_hook_source_branch=self)
4017
def peek_lock_mode(self):
4018
return self._lock_mode
2650
self._clear_cached_state_of_remote_branch_only()
2652
return self._real_branch.pull(
2653
source, overwrite=overwrite, stop_revision=stop_revision,
2654
_override_hook_target=self, **kwargs)
2657
def push(self, target, overwrite=False, stop_revision=None):
2659
return self._real_branch.push(
2660
target, overwrite=overwrite, stop_revision=stop_revision,
2661
_override_hook_source_branch=self)
4020
2663
def is_locked(self):
4021
2664
return self._lock_count >= 1
4023
def revision_id_to_dotted_revno(self, revision_id):
4024
"""Given a revision id, return its dotted revno.
4026
:return: a tuple like (1,) or (400,1,3).
4028
with self.lock_read():
4030
response = self._call(b'Branch.revision_id_to_revno',
4031
self._remote_path(), revision_id)
4032
except errors.UnknownSmartMethod:
4034
return self._real_branch.revision_id_to_dotted_revno(revision_id)
4035
if response[0] == b'ok':
4036
return tuple([int(x) for x in response[1:]])
4038
raise errors.UnexpectedSmartServerResponse(response)
4040
2667
def revision_id_to_revno(self, revision_id):
4041
"""Given a revision id on the branch mainline, return its revno.
4045
with self.lock_read():
4047
response = self._call(b'Branch.revision_id_to_revno',
4048
self._remote_path(), revision_id)
4049
except errors.UnknownSmartMethod:
4051
return self._real_branch.revision_id_to_revno(revision_id)
4052
if response[0] == b'ok':
4053
if len(response) == 2:
4054
return int(response[1])
4055
raise NoSuchRevision(self, revision_id)
4057
raise errors.UnexpectedSmartServerResponse(response)
2669
return self._real_branch.revision_id_to_revno(revision_id)
4059
2672
def set_last_revision_info(self, revno, revision_id):
4060
with self.lock_write():
4061
# XXX: These should be returned by the set_last_revision_info verb
4062
old_revno, old_revid = self.last_revision_info()
4063
self._run_pre_change_branch_tip_hooks(revno, revision_id)
4064
if not revision_id or not isinstance(revision_id, bytes):
4065
raise errors.InvalidRevisionId(
4066
revision_id=revision_id, branch=self)
4068
response = self._call(b'Branch.set_last_revision_info',
4069
self._remote_path(), self._lock_token, self._repo_lock_token,
4070
str(revno).encode('ascii'), revision_id)
4071
except errors.UnknownSmartMethod:
4073
self._clear_cached_state_of_remote_branch_only()
4074
self._real_branch.set_last_revision_info(revno, revision_id)
4075
self._last_revision_info_cache = revno, revision_id
4077
if response == (b'ok',):
4078
self._clear_cached_state()
4079
self._last_revision_info_cache = revno, revision_id
4080
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
4081
# Update the _real_branch's cache too.
4082
if self._real_branch is not None:
4083
cache = self._last_revision_info_cache
4084
self._real_branch._last_revision_info_cache = cache
4086
raise errors.UnexpectedSmartServerResponse(response)
2673
# XXX: These should be returned by the set_last_revision_info verb
2674
old_revno, old_revid = self.last_revision_info()
2675
self._run_pre_change_branch_tip_hooks(revno, revision_id)
2676
revision_id = ensure_null(revision_id)
2678
response = self._call('Branch.set_last_revision_info',
2679
self._remote_path(), self._lock_token, self._repo_lock_token,
2680
str(revno), revision_id)
2681
except errors.UnknownSmartMethod:
2683
self._clear_cached_state_of_remote_branch_only()
2684
self._real_branch.set_last_revision_info(revno, revision_id)
2685
self._last_revision_info_cache = revno, revision_id
2687
if response == ('ok',):
2688
self._clear_cached_state()
2689
self._last_revision_info_cache = revno, revision_id
2690
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2691
# Update the _real_branch's cache too.
2692
if self._real_branch is not None:
2693
cache = self._last_revision_info_cache
2694
self._real_branch._last_revision_info_cache = cache
2696
raise errors.UnexpectedSmartServerResponse(response)
4088
2699
def generate_revision_history(self, revision_id, last_rev=None,
4089
2700
other_branch=None):
4090
with self.lock_write():
4091
medium = self._client._medium
4092
if not medium._is_remote_before((1, 6)):
4093
# Use a smart method for 1.6 and above servers
4095
self._set_last_revision_descendant(revision_id, other_branch,
4096
allow_diverged=True, allow_overwrite_descendant=True)
4098
except errors.UnknownSmartMethod:
4099
medium._remember_remote_is_before((1, 6))
4100
self._clear_cached_state_of_remote_branch_only()
4101
graph = self.repository.get_graph()
4102
(last_revno, last_revid) = self.last_revision_info()
4103
known_revision_ids = [
4104
(last_revid, last_revno),
4105
(_mod_revision.NULL_REVISION, 0),
4107
if last_rev is not None:
4108
if not graph.is_ancestor(last_rev, revision_id):
4109
# our previous tip is not merged into stop_revision
4110
raise errors.DivergedBranches(self, other_branch)
4111
revno = graph.find_distance_to_null(
4112
revision_id, known_revision_ids)
4113
self.set_last_revision_info(revno, revision_id)
2701
medium = self._client._medium
2702
if not medium._is_remote_before((1, 6)):
2703
# Use a smart method for 1.6 and above servers
2705
self._set_last_revision_descendant(revision_id, other_branch,
2706
allow_diverged=True, allow_overwrite_descendant=True)
2708
except errors.UnknownSmartMethod:
2709
medium._remember_remote_is_before((1, 6))
2710
self._clear_cached_state_of_remote_branch_only()
2711
self.set_revision_history(self._lefthand_history(revision_id,
2712
last_rev=last_rev,other_branch=other_branch))
4115
2714
def set_push_location(self, location):
4116
self._set_config_location('push_location', location)
4118
def heads_to_fetch(self):
4119
if self._format._use_default_local_heads_to_fetch():
4120
# We recognise this format, and its heads-to-fetch implementation
4121
# is the default one (tip + tags). In this case it's cheaper to
4122
# just use the default implementation rather than a special RPC as
4123
# the tip and tags data is cached.
4124
return branch.Branch.heads_to_fetch(self)
4125
medium = self._client._medium
4126
if medium._is_remote_before((2, 4)):
4127
return self._vfs_heads_to_fetch()
4129
return self._rpc_heads_to_fetch()
4130
except errors.UnknownSmartMethod:
4131
medium._remember_remote_is_before((2, 4))
4132
return self._vfs_heads_to_fetch()
4134
def _rpc_heads_to_fetch(self):
4135
response = self._call(b'Branch.heads_to_fetch', self._remote_path())
4136
if len(response) != 2:
4137
raise errors.UnexpectedSmartServerResponse(response)
4138
must_fetch, if_present_fetch = response
4139
return set(must_fetch), set(if_present_fetch)
4141
def _vfs_heads_to_fetch(self):
4142
2715
self._ensure_real()
4143
return self._real_branch.heads_to_fetch()
4145
def reconcile(self, thorough=True):
4146
"""Make sure the data stored in this branch is consistent."""
4147
from .reconcile import BranchReconciler
4148
with self.lock_write():
4149
reconciler = BranchReconciler(self, thorough=thorough)
4150
return reconciler.reconcile()
2716
return self._real_branch.set_push_location(location)
4153
2719
class RemoteConfig(object):
4218
2774
medium = self._branch._client._medium
4219
2775
if medium._is_remote_before((1, 14)):
4220
2776
return self._vfs_set_option(value, name, section)
4221
if isinstance(value, dict):
4222
if medium._is_remote_before((2, 2)):
4223
return self._vfs_set_option(value, name, section)
4224
return self._set_config_option_dict(value, name, section)
4226
return self._set_config_option(value, name, section)
4228
def _set_config_option(self, value, name, section):
4230
2778
path = self._branch._remote_path()
4231
response = self._branch._client.call(b'Branch.set_config_option',
4232
path, self._branch._lock_token, self._branch._repo_lock_token,
4234
'utf8'), name.encode('utf-8'),
4235
(section or '').encode('utf-8'))
2779
response = self._branch._client.call('Branch.set_config_option',
2780
path, self._branch._lock_token, self._branch._repo_lock_token,
2781
value.encode('utf8'), name, section or '')
4236
2782
except errors.UnknownSmartMethod:
4237
medium = self._branch._client._medium
4238
2783
medium._remember_remote_is_before((1, 14))
4239
2784
return self._vfs_set_option(value, name, section)
4240
2785
if response != ():
4241
2786
raise errors.UnexpectedSmartServerResponse(response)
4243
def _serialize_option_dict(self, option_dict):
4245
for key, value in option_dict.items():
4246
if isinstance(key, text_type):
4247
key = key.encode('utf8')
4248
if isinstance(value, text_type):
4249
value = value.encode('utf8')
4250
utf8_dict[key] = value
4251
return bencode.bencode(utf8_dict)
4253
def _set_config_option_dict(self, value, name, section):
4255
path = self._branch._remote_path()
4256
serialised_dict = self._serialize_option_dict(value)
4257
response = self._branch._client.call(
4258
b'Branch.set_config_option_dict',
4259
path, self._branch._lock_token, self._branch._repo_lock_token,
4260
serialised_dict, name.encode('utf-8'), (section or '').encode('utf-8'))
4261
except errors.UnknownSmartMethod:
4262
medium = self._branch._client._medium
4263
medium._remember_remote_is_before((2, 2))
4264
return self._vfs_set_option(value, name, section)
4266
raise errors.UnexpectedSmartServerResponse(response)
4268
2788
def _real_object(self):
4269
2789
self._branch._ensure_real()
4270
2790
return self._branch._real_branch
4330
2856
def find(name):
4332
2858
return context[name]
4334
mutter('Missing key \'%s\' in context %r', name, context)
2859
except KeyError, key_err:
2860
mutter('Missing key %r in context %r', key_err.args[0], context)
4337
2862
def get_path():
4338
2863
"""Get the path from the context if present, otherwise use first error
4342
2867
return context['path']
2868
except KeyError, key_err:
4345
return err.error_args[0].decode('utf-8')
4347
mutter('Missing key \'path\' in context %r', context)
2870
return err.error_args[0]
2871
except IndexError, idx_err:
2873
'Missing key %r in context %r', key_err.args[0], context)
4349
if not isinstance(err.error_verb, bytes):
4350
raise TypeError(err.error_verb)
4352
translator = error_translators.get(err.error_verb)
4356
raise translator(err, find, get_path)
4358
translator = no_context_error_translators.get(err.error_verb)
4360
raise errors.UnknownErrorFromSmartServer(err)
4362
raise translator(err)
4365
error_translators.register(b'NoSuchRevision',
4366
lambda err, find, get_path: NoSuchRevision(
4367
find('branch'), err.error_args[0]))
4368
error_translators.register(b'nosuchrevision',
4369
lambda err, find, get_path: NoSuchRevision(
4370
find('repository'), err.error_args[0]))
4373
def _translate_nobranch_error(err, find, get_path):
4374
if len(err.error_args) >= 1:
4375
extra = err.error_args[0].decode('utf-8')
4378
return errors.NotBranchError(path=find('bzrdir').root_transport.base,
4382
error_translators.register(b'nobranch', _translate_nobranch_error)
4383
error_translators.register(b'norepository',
4384
lambda err, find, get_path: errors.NoRepositoryPresent(
4386
error_translators.register(b'UnlockableTransport',
4387
lambda err, find, get_path: errors.UnlockableTransport(
4388
find('bzrdir').root_transport))
4389
error_translators.register(b'TokenMismatch',
4390
lambda err, find, get_path: errors.TokenMismatch(
4391
find('token'), '(remote token)'))
4392
error_translators.register(b'Diverged',
4393
lambda err, find, get_path: errors.DivergedBranches(
4394
find('branch'), find('other_branch')))
4395
error_translators.register(b'NotStacked',
4396
lambda err, find, get_path: errors.NotStacked(branch=find('branch')))
4399
def _translate_PermissionDenied(err, find, get_path):
4401
if len(err.error_args) >= 2:
4402
extra = err.error_args[1].decode('utf-8')
4405
return errors.PermissionDenied(path, extra=extra)
4408
error_translators.register(b'PermissionDenied', _translate_PermissionDenied)
4409
error_translators.register(b'ReadError',
4410
lambda err, find, get_path: errors.ReadError(get_path()))
4411
error_translators.register(b'NoSuchFile',
4412
lambda err, find, get_path: errors.NoSuchFile(get_path()))
4413
error_translators.register(b'TokenLockingNotSupported',
4414
lambda err, find, get_path: errors.TokenLockingNotSupported(
4415
find('repository')))
4416
error_translators.register(b'UnsuspendableWriteGroup',
4417
lambda err, find, get_path: errors.UnsuspendableWriteGroup(
4418
repository=find('repository')))
4419
error_translators.register(b'UnresumableWriteGroup',
4420
lambda err, find, get_path: errors.UnresumableWriteGroup(
4421
repository=find('repository'), write_groups=err.error_args[0],
4422
reason=err.error_args[1]))
4423
no_context_error_translators.register(b'GhostRevisionsHaveNoRevno',
4424
lambda err: errors.GhostRevisionsHaveNoRevno(*err.error_args))
4425
no_context_error_translators.register(b'IncompatibleRepositories',
4426
lambda err: errors.IncompatibleRepositories(
4427
err.error_args[0].decode('utf-8'), err.error_args[1].decode('utf-8'), err.error_args[2].decode('utf-8')))
4428
no_context_error_translators.register(b'LockContention',
4429
lambda err: errors.LockContention('(remote lock)'))
4430
no_context_error_translators.register(b'LockFailed',
4431
lambda err: errors.LockFailed(err.error_args[0].decode('utf-8'), err.error_args[1].decode('utf-8')))
4432
no_context_error_translators.register(b'TipChangeRejected',
4433
lambda err: errors.TipChangeRejected(err.error_args[0].decode('utf8')))
4434
no_context_error_translators.register(b'UnstackableBranchFormat',
4435
lambda err: branch.UnstackableBranchFormat(*err.error_args))
4436
no_context_error_translators.register(b'UnstackableRepositoryFormat',
4437
lambda err: errors.UnstackableRepositoryFormat(*err.error_args))
4438
no_context_error_translators.register(b'FileExists',
4439
lambda err: errors.FileExists(err.error_args[0].decode('utf-8')))
4440
no_context_error_translators.register(b'DirectoryNotEmpty',
4441
lambda err: errors.DirectoryNotEmpty(err.error_args[0].decode('utf-8')))
4442
no_context_error_translators.register(b'UnknownFormat',
4443
lambda err: errors.UnknownFormatError(
4444
err.error_args[0].decode('ascii'), err.error_args[0].decode('ascii')))
4445
no_context_error_translators.register(b'InvalidURL',
4446
lambda err: urlutils.InvalidURL(
4447
err.error_args[0].decode('utf-8'), err.error_args[1].decode('ascii')))
4450
def _translate_short_readv_error(err):
4451
args = err.error_args
4452
return errors.ShortReadvError(
4453
args[0].decode('utf-8'),
4454
int(args[1].decode('ascii')), int(args[2].decode('ascii')),
4455
int(args[3].decode('ascii')))
4458
no_context_error_translators.register(b'ShortReadvError',
4459
_translate_short_readv_error)
4462
def _translate_unicode_error(err):
4463
encoding = err.error_args[0].decode('ascii')
4464
val = err.error_args[1].decode('utf-8')
4465
start = int(err.error_args[2].decode('ascii'))
4466
end = int(err.error_args[3].decode('ascii'))
4467
reason = err.error_args[4].decode('utf-8')
4468
if val.startswith('u:'):
4469
val = val[2:].decode('utf-8')
4470
elif val.startswith('s:'):
4471
val = val[2:].decode('base64')
4472
if err.error_verb == 'UnicodeDecodeError':
4473
raise UnicodeDecodeError(encoding, val, start, end, reason)
4474
elif err.error_verb == 'UnicodeEncodeError':
4475
raise UnicodeEncodeError(encoding, val, start, end, reason)
4478
no_context_error_translators.register(b'UnicodeEncodeError',
4479
_translate_unicode_error)
4480
no_context_error_translators.register(b'UnicodeDecodeError',
4481
_translate_unicode_error)
4482
no_context_error_translators.register(b'ReadOnlyError',
4483
lambda err: errors.TransportNotPossible('readonly transport'))
4484
no_context_error_translators.register(b'MemoryError',
4485
lambda err: errors.BzrError("remote server out of memory\n"
4486
"Retry non-remotely, or contact the server admin for details."))
4487
no_context_error_translators.register(b'RevisionNotPresent',
4488
lambda err: errors.RevisionNotPresent(err.error_args[0].decode('utf-8'), err.error_args[1].decode('utf-8')))
4490
no_context_error_translators.register(b'BzrCheckError',
4491
lambda err: errors.BzrCheckError(msg=err.error_args[0].decode('utf-8')))
2876
if err.error_verb == 'IncompatibleRepositories':
2877
raise errors.IncompatibleRepositories(err.error_args[0],
2878
err.error_args[1], err.error_args[2])
2879
elif err.error_verb == 'NoSuchRevision':
2880
raise NoSuchRevision(find('branch'), err.error_args[0])
2881
elif err.error_verb == 'nosuchrevision':
2882
raise NoSuchRevision(find('repository'), err.error_args[0])
2883
elif err.error_verb == 'nobranch':
2884
if len(err.error_args) >= 1:
2885
extra = err.error_args[0]
2888
raise errors.NotBranchError(path=find('bzrdir').root_transport.base,
2890
elif err.error_verb == 'norepository':
2891
raise errors.NoRepositoryPresent(find('bzrdir'))
2892
elif err.error_verb == 'LockContention':
2893
raise errors.LockContention('(remote lock)')
2894
elif err.error_verb == 'UnlockableTransport':
2895
raise errors.UnlockableTransport(find('bzrdir').root_transport)
2896
elif err.error_verb == 'LockFailed':
2897
raise errors.LockFailed(err.error_args[0], err.error_args[1])
2898
elif err.error_verb == 'TokenMismatch':
2899
raise errors.TokenMismatch(find('token'), '(remote token)')
2900
elif err.error_verb == 'Diverged':
2901
raise errors.DivergedBranches(find('branch'), find('other_branch'))
2902
elif err.error_verb == 'TipChangeRejected':
2903
raise errors.TipChangeRejected(err.error_args[0].decode('utf8'))
2904
elif err.error_verb == 'UnstackableBranchFormat':
2905
raise errors.UnstackableBranchFormat(*err.error_args)
2906
elif err.error_verb == 'UnstackableRepositoryFormat':
2907
raise errors.UnstackableRepositoryFormat(*err.error_args)
2908
elif err.error_verb == 'NotStacked':
2909
raise errors.NotStacked(branch=find('branch'))
2910
elif err.error_verb == 'PermissionDenied':
2912
if len(err.error_args) >= 2:
2913
extra = err.error_args[1]
2916
raise errors.PermissionDenied(path, extra=extra)
2917
elif err.error_verb == 'ReadError':
2919
raise errors.ReadError(path)
2920
elif err.error_verb == 'NoSuchFile':
2922
raise errors.NoSuchFile(path)
2923
elif err.error_verb == 'FileExists':
2924
raise errors.FileExists(err.error_args[0])
2925
elif err.error_verb == 'DirectoryNotEmpty':
2926
raise errors.DirectoryNotEmpty(err.error_args[0])
2927
elif err.error_verb == 'ShortReadvError':
2928
args = err.error_args
2929
raise errors.ShortReadvError(
2930
args[0], int(args[1]), int(args[2]), int(args[3]))
2931
elif err.error_verb in ('UnicodeEncodeError', 'UnicodeDecodeError'):
2932
encoding = str(err.error_args[0]) # encoding must always be a string
2933
val = err.error_args[1]
2934
start = int(err.error_args[2])
2935
end = int(err.error_args[3])
2936
reason = str(err.error_args[4]) # reason must always be a string
2937
if val.startswith('u:'):
2938
val = val[2:].decode('utf-8')
2939
elif val.startswith('s:'):
2940
val = val[2:].decode('base64')
2941
if err.error_verb == 'UnicodeDecodeError':
2942
raise UnicodeDecodeError(encoding, val, start, end, reason)
2943
elif err.error_verb == 'UnicodeEncodeError':
2944
raise UnicodeEncodeError(encoding, val, start, end, reason)
2945
elif err.error_verb == 'ReadOnlyError':
2946
raise errors.TransportNotPossible('readonly transport')
2947
raise errors.UnknownErrorFromSmartServer(err)