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