105
74
return self._client.call_with_body_bytes_expecting_body(
106
75
method, args, body_bytes)
107
except errors.ErrorFromSmartServer as err:
76
except errors.ErrorFromSmartServer, err:
108
77
self._translate_error(err, **err_context)
111
80
def response_tuple_to_repo_format(response):
112
81
"""Convert a response tuple describing a repository format to a format."""
113
82
format = RemoteRepositoryFormat()
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')
83
format._rich_root_data = (response[0] == 'yes')
84
format._supports_tree_reference = (response[1] == 'yes')
85
format._supports_external_lookups = (response[2] == 'yes')
117
86
format._network_name = response[3]
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):
90
# Note: RemoteBzrDirFormat is in bzrdir.py
92
class RemoteBzrDir(BzrDir, _RpcHelper):
428
93
"""Control directory on a remote server, accessed via bzr:// or similar."""
430
95
def __init__(self, transport, format, _client=None, _force_probe=False):
564
187
medium = self._client._medium
565
188
if medium._is_remote_before((1, 13)):
566
189
return self._vfs_cloning_metadir(require_stacking=require_stacking)
567
verb = b'BzrDir.cloning_metadir'
190
verb = 'BzrDir.cloning_metadir'
568
191
if require_stacking:
572
195
path = self._path_for_remote_call(self._client)
574
197
response = self._call(verb, path, stacking)
575
198
except errors.UnknownSmartMethod:
576
199
medium._remember_remote_is_before((1, 13))
577
200
return self._vfs_cloning_metadir(require_stacking=require_stacking)
578
except errors.UnknownErrorFromSmartServer as err:
579
if err.error_tuple != (b'BranchReference',):
201
except errors.UnknownErrorFromSmartServer, err:
202
if err.error_tuple != ('BranchReference',):
581
204
# We need to resolve the branch reference to determine the
582
205
# cloning_metadir. This causes unnecessary RPCs to open the
583
206
# referenced branch (and bzrdir, etc) but only when the caller
584
207
# didn't already resolve the branch reference.
585
208
referenced_branch = self.open_branch()
586
return referenced_branch.controldir.cloning_metadir()
209
return referenced_branch.bzrdir.cloning_metadir()
587
210
if len(response) != 3:
588
211
raise errors.UnexpectedSmartServerResponse(response)
589
212
control_name, repo_name, branch_info = response
590
213
if len(branch_info) != 2:
591
214
raise errors.UnexpectedSmartServerResponse(response)
592
215
branch_ref, branch_name = branch_info
594
format = controldir.network_format_registry.get(control_name)
596
raise errors.UnknownFormatError(
597
kind='control', format=control_name)
216
format = bzrdir.network_format_registry.get(control_name)
601
format.repository_format = _mod_repository.network_format_registry.get(
604
raise errors.UnknownFormatError(kind='repository',
606
if branch_ref == b'ref':
218
format.repository_format = repository.network_format_registry.get(
220
if branch_ref == 'ref':
607
221
# XXX: we need possible_transports here to avoid reopening the
608
222
# connection to the referenced location
609
ref_bzrdir = _mod_bzrdir.BzrDir.open(branch_name)
223
ref_bzrdir = BzrDir.open(branch_name)
610
224
branch_format = ref_bzrdir.cloning_metadir().get_branch_format()
611
225
format.set_branch_format(branch_format)
612
elif branch_ref == b'branch':
226
elif branch_ref == 'branch':
615
branch_format = branch.network_format_registry.get(
618
raise errors.UnknownFormatError(kind='branch',
620
format.set_branch_format(branch_format)
228
format.set_branch_format(
229
branch.network_format_registry.get(branch_name))
622
231
raise errors.UnexpectedSmartServerResponse(response)
707
281
b = self.open_branch(name=name)
710
def branch_names(self):
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.branch_names()
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')
727
def get_branches(self, possible_transports=None, ignore_fallbacks=False):
728
path = self._path_for_remote_call(self._client)
730
response, handler = self._call_expecting_body(
731
b'BzrDir.get_branches', path)
732
except errors.UnknownSmartMethod:
734
return self._real_bzrdir.get_branches()
735
if response[0] != b"success":
736
raise errors.UnexpectedSmartServerResponse(response)
737
body = bencode.bdecode(handler.read_body_bytes())
739
for name, value in viewitems(body):
740
name = name.decode('utf-8')
741
ret[name] = self._open_branch(
742
name, value[0].decode('ascii'), value[1],
743
possible_transports=possible_transports,
744
ignore_fallbacks=ignore_fallbacks)
747
def set_branch_reference(self, target_branch, name=None):
748
"""See BzrDir.set_branch_reference()."""
750
name = self._get_selected_branch()
752
raise errors.NoColocatedBranchSupport(self)
754
return self._real_bzrdir.set_branch_reference(target_branch, name=name)
756
284
def get_branch_reference(self, name=None):
757
285
"""See BzrDir.get_branch_reference()."""
759
name = self._get_selected_branch()
287
# XXX JRV20100304: Support opening colocated branches
761
288
raise errors.NoColocatedBranchSupport(self)
762
289
response = self._get_branch_reference()
763
290
if response[0] == 'ref':
764
return response[1].decode('utf-8')
768
295
def _get_branch_reference(self):
769
"""Get branch reference information
771
:return: Tuple with (kind, location_or_format)
772
if kind == 'ref', then location_or_format contains a location
773
otherwise, it contains a format name
775
296
path = self._path_for_remote_call(self._client)
776
297
medium = self._client._medium
777
298
candidate_calls = [
778
(b'BzrDir.open_branchV3', (2, 1)),
779
(b'BzrDir.open_branchV2', (1, 13)),
780
(b'BzrDir.open_branch', None),
299
('BzrDir.open_branchV3', (2, 1)),
300
('BzrDir.open_branchV2', (1, 13)),
301
('BzrDir.open_branch', None),
782
303
for verb, required_version in candidate_calls:
783
304
if required_version and medium._is_remote_before(required_version):
790
311
medium._remember_remote_is_before(required_version)
793
if verb == b'BzrDir.open_branch':
794
if response[0] != b'ok':
314
if verb == 'BzrDir.open_branch':
315
if response[0] != 'ok':
795
316
raise errors.UnexpectedSmartServerResponse(response)
796
if response[1] != b'':
317
if response[1] != '':
797
318
return ('ref', response[1])
799
return ('branch', b'')
800
if response[0] not in (b'ref', b'branch'):
320
return ('branch', '')
321
if response[0] not in ('ref', 'branch'):
801
322
raise errors.UnexpectedSmartServerResponse(response)
802
return (response[0].decode('ascii'), response[1])
804
325
def _get_tree_branch(self, name=None):
805
326
"""See BzrDir._get_tree_branch()."""
806
327
return None, self.open_branch(name=name)
808
def _open_branch(self, name, kind, location_or_format,
809
ignore_fallbacks=False, possible_transports=None):
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':
811
340
# a branch reference, use the existing BranchReference logic.
812
341
format = BranchReferenceFormat()
813
ref_loc = urlutils.join(self.user_url, location_or_format.decode('utf-8'))
814
342
return format.open(self, name=name, _found=True,
816
ignore_fallbacks=ignore_fallbacks,
817
possible_transports=possible_transports)
818
branch_format_name = location_or_format
343
location=response[1], ignore_fallbacks=ignore_fallbacks)
344
branch_format_name = response[1]
819
345
if not branch_format_name:
820
346
branch_format_name = None
821
347
format = RemoteBranchFormat(network_name=branch_format_name)
822
348
return RemoteBranch(self, self.find_repository(), format=format,
823
setup_stacking=not ignore_fallbacks, name=name,
824
possible_transports=possible_transports)
826
def open_branch(self, name=None, unsupported=False,
827
ignore_fallbacks=False, possible_transports=None):
829
name = self._get_selected_branch()
831
raise errors.NoColocatedBranchSupport(self)
833
raise NotImplementedError(
834
'unsupported flag support not implemented yet.')
835
if self._next_open_branch_result is not None:
836
# See create_branch for details.
837
result = self._next_open_branch_result
838
self._next_open_branch_result = None
840
response = self._get_branch_reference()
841
return self._open_branch(name, response[0], response[1],
842
possible_transports=possible_transports,
843
ignore_fallbacks=ignore_fallbacks)
349
setup_stacking=not ignore_fallbacks, name=name)
845
351
def _open_repo_v1(self, path):
846
verb = b'BzrDir.find_repository'
352
verb = 'BzrDir.find_repository'
847
353
response = self._call(verb, path)
848
if response[0] != b'ok':
354
if response[0] != 'ok':
849
355
raise errors.UnexpectedSmartServerResponse(response)
850
356
# servers that only support the v1 method don't support external
851
357
# references either.
852
358
self._ensure_real()
853
359
repo = self._real_bzrdir.open_repository()
854
response = response + (b'no', repo._format.network_name())
360
response = response + ('no', repo._format.network_name())
855
361
return response, repo
857
363
def _open_repo_v2(self, path):
858
verb = b'BzrDir.find_repositoryV2'
364
verb = 'BzrDir.find_repositoryV2'
859
365
response = self._call(verb, path)
860
if response[0] != b'ok':
366
if response[0] != 'ok':
861
367
raise errors.UnexpectedSmartServerResponse(response)
862
368
self._ensure_real()
863
369
repo = self._real_bzrdir.open_repository()
956
445
"""Upgrading of remote bzrdirs is not supported yet."""
959
def needs_format_conversion(self, format):
448
def needs_format_conversion(self, format=None):
960
449
"""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)
963
461
def _get_config(self):
964
462
return RemoteBzrDirConfig(self)
966
def _get_config_store(self):
967
return RemoteControlStore(self)
970
class RemoteInventoryTree(InventoryRevisionTree):
972
def __init__(self, repository, inv, revision_id):
973
super(RemoteInventoryTree, self).__init__(repository, inv, revision_id)
975
def archive(self, format, name, root=None, subdir=None, force_mtime=None):
976
ret = self._repository._revision_archive(
977
self.get_revision_id(), format, name, root, subdir,
978
force_mtime=force_mtime)
980
return super(RemoteInventoryTree, self).archive(
981
format, name, root, subdir, force_mtime=force_mtime)
984
def annotate_iter(self, path,
985
default_revision=_mod_revision.CURRENT_REVISION):
986
"""Return an iterator of revision_id, line tuples.
988
For working trees (and mutable trees in general), the special
989
revision_id 'current:' will be used for lines that are new in this
990
tree, e.g. uncommitted changes.
991
:param default_revision: For lines that don't match a basis, mark them
992
with this revision id. Not all implementations will make use of
995
ret = self._repository._annotate_file_revision(
996
self.get_revision_id(), path, file_id=None,
997
default_revision=default_revision)
999
return super(RemoteInventoryTree, self).annotate_iter(
1000
path, default_revision=default_revision)
1004
class RemoteRepositoryFormat(vf_repository.VersionedFileRepositoryFormat):
465
class RemoteRepositoryFormat(repository.RepositoryFormat):
1005
466
"""Format for repositories accessed over a _SmartClient.
1007
468
Instances of this repository are represented by RemoteRepository
1094
533
self._custom_format.supports_tree_reference
1095
534
return self._supports_tree_reference
1098
def revision_graph_can_have_wrong_parents(self):
1099
if self._revision_graph_can_have_wrong_parents is None:
1101
self._revision_graph_can_have_wrong_parents = \
1102
self._custom_format.revision_graph_can_have_wrong_parents
1103
return self._revision_graph_can_have_wrong_parents
1105
def _vfs_initialize(self, a_controldir, shared):
536
def _vfs_initialize(self, a_bzrdir, shared):
1106
537
"""Helper for common code in initialize."""
1107
538
if self._custom_format:
1108
539
# Custom format requested
1109
result = self._custom_format.initialize(
1110
a_controldir, shared=shared)
540
result = self._custom_format.initialize(a_bzrdir, shared=shared)
1111
541
elif self._creating_bzrdir is not None:
1112
542
# Use the format that the repository we were created to back
1114
544
prior_repo = self._creating_bzrdir.open_repository()
1115
545
prior_repo._ensure_real()
1116
546
result = prior_repo._real_repository._format.initialize(
1117
a_controldir, shared=shared)
547
a_bzrdir, shared=shared)
1119
549
# assume that a_bzr is a RemoteBzrDir but the smart server didn't
1120
550
# support remote initialization.
1121
551
# We delegate to a real object at this point (as RemoteBzrDir
1122
552
# delegate to the repository format which would lead to infinite
1123
# recursion if we just called a_controldir.create_repository.
1124
a_controldir._ensure_real()
1125
result = a_controldir._real_bzrdir.create_repository(shared=shared)
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)
1126
556
if not isinstance(result, RemoteRepository):
1127
return self.open(a_controldir)
557
return self.open(a_bzrdir)
1131
def initialize(self, a_controldir, shared=False):
561
def initialize(self, a_bzrdir, shared=False):
1132
562
# Being asked to create on a non RemoteBzrDir:
1133
if not isinstance(a_controldir, RemoteBzrDir):
1134
return self._vfs_initialize(a_controldir, shared)
1135
medium = a_controldir._client._medium
563
if not isinstance(a_bzrdir, RemoteBzrDir):
564
return self._vfs_initialize(a_bzrdir, shared)
565
medium = a_bzrdir._client._medium
1136
566
if medium._is_remote_before((1, 13)):
1137
return self._vfs_initialize(a_controldir, shared)
567
return self._vfs_initialize(a_bzrdir, shared)
1138
568
# Creating on a remote bzr dir.
1139
569
# 1) get the network name to use.
1140
570
if self._custom_format:
1142
572
elif self._network_name:
1143
573
network_name = self._network_name
1145
# Select the current breezy default and ask for that.
1146
reference_bzrdir_format = controldir.format_registry.get(
575
# Select the current bzrlib default and ask for that.
576
reference_bzrdir_format = bzrdir.format_registry.get('default')()
1148
577
reference_format = reference_bzrdir_format.repository_format
1149
578
network_name = reference_format.network_name()
1150
579
# 2) try direct creation via RPC
1151
path = a_controldir._path_for_remote_call(a_controldir._client)
1152
verb = b'BzrDir.create_repository'
580
path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
581
verb = 'BzrDir.create_repository'
1154
shared_str = b'True'
1156
shared_str = b'False'
1158
response = a_controldir._call(verb, path, network_name, shared_str)
587
response = a_bzrdir._call(verb, path, network_name, shared_str)
1159
588
except errors.UnknownSmartMethod:
1160
589
# Fallback - use vfs methods
1161
590
medium._remember_remote_is_before((1, 13))
1162
return self._vfs_initialize(a_controldir, shared)
591
return self._vfs_initialize(a_bzrdir, shared)
1164
593
# Turn the response into a RemoteRepository object.
1165
594
format = response_tuple_to_repo_format(response[1:])
1166
595
# Used to support creating a real format instance when needed.
1167
format._creating_bzrdir = a_controldir
1168
remote_repo = RemoteRepository(a_controldir, format)
596
format._creating_bzrdir = a_bzrdir
597
remote_repo = RemoteRepository(a_bzrdir, format)
1169
598
format._creating_repo = remote_repo
1170
599
return remote_repo
1172
def open(self, a_controldir):
1173
if not isinstance(a_controldir, RemoteBzrDir):
1174
raise AssertionError('%r is not a RemoteBzrDir' % (a_controldir,))
1175
return a_controldir.open_repository()
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()
1177
606
def _ensure_real(self):
1178
607
if self._custom_format is None:
1180
self._custom_format = _mod_repository.network_format_registry.get(
1183
raise errors.UnknownFormatError(kind='repository',
1184
format=self._network_name)
608
self._custom_format = repository.network_format_registry.get(
1187
612
def _fetch_order(self):
1551
913
# TODO: Move to RepositoryBase and unify with the regular Repository
1552
914
# one; unfortunately the tests rely on slightly different behaviour at
1553
915
# present -- mbp 20090710
1554
return (self.__class__ is other.__class__
1555
and self.controldir.transport.base == other.controldir.transport.base)
916
return (self.__class__ is other.__class__ and
917
self.bzrdir.transport.base == other.bzrdir.transport.base)
1557
919
def get_graph(self, other_repository=None):
1558
920
"""Return the graph for this repository format"""
1559
921
parents_provider = self._make_parents_provider(other_repository)
1560
922
return graph.Graph(parents_provider)
1562
925
def get_known_graph_ancestry(self, revision_ids):
1563
926
"""Return the known graph for a set of revision ids and their ancestors.
1565
with self.lock_read():
1566
revision_graph = dict(((key, value) for key, value in
1567
self.get_graph().iter_ancestry(revision_ids) if value is not None))
1568
revision_graph = _mod_repository._strip_NULL_ghosts(revision_graph)
1569
return graph.KnownGraph(revision_graph)
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)
1571
933
def gather_stats(self, revid=None, committers=None):
1572
934
"""See Repository.gather_stats()."""
1573
path = self.controldir._path_for_remote_call(self._client)
935
path = self.bzrdir._path_for_remote_call(self._client)
1574
936
# revid can be None to indicate no revisions, not just NULL_REVISION
1575
if revid is None or _mod_revision.is_null(revid):
937
if revid is None or revision.is_null(revid):
1578
940
fmt_revid = revid
1579
941
if committers is None or not committers:
1580
fmt_committers = b'no'
942
fmt_committers = 'no'
1582
fmt_committers = b'yes'
944
fmt_committers = 'yes'
1583
945
response_tuple, response_handler = self._call_expecting_body(
1584
b'Repository.gather_stats', path, fmt_revid, fmt_committers)
1585
if response_tuple[0] != b'ok':
946
'Repository.gather_stats', path, fmt_revid, fmt_committers)
947
if response_tuple[0] != 'ok':
1586
948
raise errors.UnexpectedSmartServerResponse(response_tuple)
1588
950
body = response_handler.read_body_bytes()
1590
for line in body.split(b'\n'):
952
for line in body.split('\n'):
1593
key, val_text = line.split(b':')
1594
key = key.decode('ascii')
955
key, val_text = line.split(':')
1595
956
if key in ('revisions', 'size', 'committers'):
1596
957
result[key] = int(val_text)
1597
958
elif key in ('firstrev', 'latestrev'):
1598
values = val_text.split(b' ')[1:]
1599
result[key] = (float(values[0]), int(values[1]))
959
values = val_text.split(' ')[1:]
960
result[key] = (float(values[0]), long(values[1]))
1880
1204
raise errors.UnexpectedSmartServerResponse(response)
1882
1206
def sprout(self, to_bzrdir, revision_id=None):
1883
"""Create a descendent repository for new development.
1885
Unlike clone, this does not copy the settings of the repository.
1887
with self.lock_read():
1888
dest_repo = self._create_sprouting_repo(to_bzrdir, shared=False)
1889
dest_repo.fetch(self, revision_id=revision_id)
1892
def _create_sprouting_repo(self, a_controldir, shared):
1893
if not isinstance(a_controldir._format, self.controldir._format.__class__):
1894
# use target default format.
1895
dest_repo = a_controldir.create_repository()
1897
# Most control formats need the repository to be specifically
1898
# created, but on some old all-in-one formats it's not needed
1900
dest_repo = self._format.initialize(
1901
a_controldir, shared=shared)
1902
except errors.UninitializableFormat:
1903
dest_repo = a_controldir.open_repository()
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)
1904
1212
return dest_repo
1906
# These methods are just thin shims to the VFS object for now.
1214
### These methods are just thin shims to the VFS object for now.
1908
1216
def revision_tree(self, revision_id):
1909
with self.lock_read():
1910
revision_id = _mod_revision.ensure_null(revision_id)
1911
if revision_id == _mod_revision.NULL_REVISION:
1912
return InventoryRevisionTree(self,
1913
Inventory(root_id=None), _mod_revision.NULL_REVISION)
1915
return list(self.revision_trees([revision_id]))[0]
1218
return self._real_repository.revision_tree(revision_id)
1917
1220
def get_serializer_format(self):
1918
path = self.controldir._path_for_remote_call(self._client)
1920
response = self._call(b'VersionedFileRepository.get_serializer_format',
1922
except errors.UnknownSmartMethod:
1924
return self._real_repository.get_serializer_format()
1925
if response[0] != b'ok':
1926
raise errors.UnexpectedSmartServerResponse(response)
1222
return self._real_repository.get_serializer_format()
1929
1224
def get_commit_builder(self, branch, parents, config, timestamp=None,
1930
1225
timezone=None, committer=None, revprops=None,
1931
revision_id=None, lossy=False):
1932
"""Obtain a CommitBuilder for this repository.
1934
:param branch: Branch to commit to.
1935
:param parents: Revision ids of the parents of the new revision.
1936
:param config: Configuration to use.
1937
:param timestamp: Optional timestamp recorded for commit.
1938
:param timezone: Optional timezone for timestamp.
1939
:param committer: Optional committer to set for commit.
1940
:param revprops: Optional dictionary of revision properties.
1941
:param revision_id: Optional revision id.
1942
:param lossy: Whether to discard data that can not be natively
1943
represented, when pushing to a foreign VCS
1945
if self._fallback_repositories and not self._format.supports_chks:
1946
raise errors.BzrError("Cannot commit directly to a stacked branch"
1947
" in pre-2a formats. See "
1948
"https://bugs.launchpad.net/bzr/+bug/375013 for details.")
1949
commit_builder_kls = vf_repository.VersionedFileCommitBuilder
1950
result = commit_builder_kls(self, parents, config,
1951
timestamp, timezone, committer, revprops, revision_id,
1953
self.start_write_group()
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)
1956
1236
def add_fallback_repository(self, repository):
1957
1237
"""Add a repository to use for looking up data not held locally.
1996
1275
return self._real_repository.add_inventory(revid, inv, parents)
1998
1277
def add_inventory_by_delta(self, basis_revision_id, delta, new_revision_id,
1999
parents, basis_inv=None, propagate_caches=False):
1278
parents, basis_inv=None, propagate_caches=False):
2000
1279
self._ensure_real()
2001
1280
return self._real_repository.add_inventory_by_delta(basis_revision_id,
2002
delta, new_revision_id, parents, basis_inv=basis_inv,
2003
propagate_caches=propagate_caches)
2005
def add_revision(self, revision_id, rev, inv=None):
2006
_mod_revision.check_not_reserved_id(revision_id)
2007
key = (revision_id,)
2008
# check inventory present
2009
if not self.inventories.get_parent_map([key]):
2011
raise errors.WeaveRevisionNotPresent(revision_id,
2014
# yes, this is not suitable for adding with ghosts.
2015
rev.inventory_sha1 = self.add_inventory(revision_id, inv,
2018
rev.inventory_sha1 = self.inventories.get_sha1s([key])[key]
2019
self._add_revision(rev)
2021
def _add_revision(self, rev):
2022
if self._real_repository is not None:
2023
return self._real_repository._add_revision(rev)
2024
lines = self._serializer.write_revision_to_lines(rev)
2025
key = (rev.revision_id,)
2026
parents = tuple((parent,) for parent in rev.parent_ids)
2027
self._write_group_tokens, missing_keys = self._get_sink().insert_stream(
2028
[('revisions', [ChunkedContentFactory(key, parents, None, lines, chunks_are_lines=True)])],
2029
self._format, self._write_group_tokens)
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)
2031
1290
def get_inventory(self, revision_id):
2032
with self.lock_read():
2033
return list(self.iter_inventories([revision_id]))[0]
2035
def _iter_inventories_rpc(self, revision_ids, ordering):
2036
if ordering is None:
2037
ordering = 'unordered'
2038
path = self.controldir._path_for_remote_call(self._client)
2039
body = b"\n".join(revision_ids)
2040
response_tuple, response_handler = (
2041
self._call_with_body_bytes_expecting_body(
2042
b"VersionedFileRepository.get_inventories",
2043
(path, ordering.encode('ascii')), body))
2044
if response_tuple[0] != b"ok":
2045
raise errors.UnexpectedSmartServerResponse(response_tuple)
2046
deserializer = inventory_delta.InventoryDeltaDeserializer()
2047
byte_stream = response_handler.read_streamed_body()
2048
decoded = smart_repo._byte_stream_to_stream(byte_stream)
2050
# no results whatsoever
2052
src_format, stream = decoded
2053
if src_format.network_name() != self._format.network_name():
2054
raise AssertionError(
2055
"Mismatched RemoteRepository and stream src %r, %r" % (
2056
src_format.network_name(), self._format.network_name()))
2057
# ignore the src format, it's not really relevant
2058
prev_inv = Inventory(root_id=None,
2059
revision_id=_mod_revision.NULL_REVISION)
2060
# there should be just one substream, with inventory deltas
2062
substream_kind, substream = next(stream)
2063
except StopIteration:
2065
if substream_kind != "inventory-deltas":
2066
raise AssertionError(
2067
"Unexpected stream %r received" % substream_kind)
2068
for record in substream:
2069
(parent_id, new_id, versioned_root, tree_references, invdelta) = (
2070
deserializer.parse_text_bytes(record.get_bytes_as("lines")))
2071
if parent_id != prev_inv.revision_id:
2072
raise AssertionError("invalid base %r != %r" % (parent_id,
2073
prev_inv.revision_id))
2074
inv = prev_inv.create_by_apply_delta(invdelta, new_id)
2075
yield inv, inv.revision_id
2078
def _iter_inventories_vfs(self, revision_ids, ordering=None):
2079
1291
self._ensure_real()
2080
return self._real_repository._iter_inventories(revision_ids, ordering)
1292
return self._real_repository.get_inventory(revision_id)
2082
1294
def iter_inventories(self, revision_ids, ordering=None):
2083
"""Get many inventories by revision_ids.
2085
This will buffer some or all of the texts used in constructing the
2086
inventories in memory, but will only parse a single inventory at a
2089
:param revision_ids: The expected revision ids of the inventories.
2090
:param ordering: optional ordering, e.g. 'topological'. If not
2091
specified, the order of revision_ids will be preserved (by
2092
buffering if necessary).
2093
:return: An iterator of inventories.
2095
if ((None in revision_ids) or
2096
(_mod_revision.NULL_REVISION in revision_ids)):
2097
raise ValueError('cannot get null revision inventory')
2098
for inv, revid in self._iter_inventories(revision_ids, ordering):
2100
raise errors.NoSuchRevision(self, revid)
2103
def _iter_inventories(self, revision_ids, ordering=None):
2104
if len(revision_ids) == 0:
2106
missing = set(revision_ids)
2107
if ordering is None:
2108
order_as_requested = True
2110
order = list(revision_ids)
2112
next_revid = order.pop()
2114
order_as_requested = False
2115
if ordering != 'unordered' and self._fallback_repositories:
2116
raise ValueError('unsupported ordering %r' % ordering)
2117
iter_inv_fns = [self._iter_inventories_rpc] + [
2118
fallback._iter_inventories for fallback in
2119
self._fallback_repositories]
2121
for iter_inv in iter_inv_fns:
2122
request = [revid for revid in revision_ids if revid in missing]
2123
for inv, revid in iter_inv(request, ordering):
2126
missing.remove(inv.revision_id)
2127
if ordering != 'unordered':
2131
if order_as_requested:
2132
# Yield as many results as we can while preserving order.
2133
while next_revid in invs:
2134
inv = invs.pop(next_revid)
2135
yield inv, inv.revision_id
2137
next_revid = order.pop()
2139
# We still want to fully consume the stream, just
2140
# in case it is not actually finished at this point
2143
except errors.UnknownSmartMethod:
2144
for inv, revid in self._iter_inventories_vfs(revision_ids, ordering):
2148
if order_as_requested:
2149
if next_revid is not None:
2150
yield None, next_revid
2153
yield invs.get(revid), revid
2156
yield None, missing.pop()
1296
return self._real_repository.iter_inventories(revision_ids, ordering)
2158
1299
def get_revision(self, revision_id):
2159
with self.lock_read():
2160
return self.get_revisions([revision_id])[0]
1301
return self._real_repository.get_revision(revision_id)
2162
1303
def get_transaction(self):
2163
1304
self._ensure_real()
2164
1305
return self._real_repository.get_transaction()
2166
def clone(self, a_controldir, revision_id=None):
2167
with self.lock_read():
2168
dest_repo = self._create_sprouting_repo(
2169
a_controldir, shared=self.is_shared())
2170
self.copy_content_into(dest_repo, revision_id)
1308
def clone(self, a_bzrdir, revision_id=None):
1310
return self._real_repository.clone(a_bzrdir, revision_id=revision_id)
2173
1312
def make_working_trees(self):
2174
1313
"""See Repository.make_working_trees"""
2175
path = self.controldir._path_for_remote_call(self._client)
2177
response = self._call(b'Repository.make_working_trees', path)
2178
except errors.UnknownSmartMethod:
2180
return self._real_repository.make_working_trees()
2181
if response[0] not in (b'yes', b'no'):
2182
raise SmartProtocolError(
2183
'unexpected response code %s' % (response,))
2184
return response[0] == b'yes'
1315
return self._real_repository.make_working_trees()
2186
1317
def refresh_data(self):
2187
1318
"""Re-read any data needed to synchronise with disk.
2189
1320
This method is intended to be called after another repository instance
2190
1321
(such as one used by a smart server) has inserted data into the
2191
1322
repository. On all repositories this will work outside of write groups.
2192
Some repository formats (pack and newer for breezy native formats)
1323
Some repository formats (pack and newer for bzrlib native formats)
2193
1324
support refresh_data inside write groups. If called inside a write
2194
1325
group on a repository that does not support refreshing in a write group
2195
1326
IsInWriteGroupError will be raised.
2197
1328
if self._real_repository is not None:
2198
1329
self._real_repository.refresh_data()
2199
# Refresh the parents cache for this object
2200
self._unstacked_provider.disable_cache()
2201
self._unstacked_provider.enable_cache()
2203
1331
def revision_ids_to_search_result(self, result_set):
2204
1332
"""Convert a set of revision ids to a graph SearchResult."""
2205
1333
result_parents = set()
2206
for parents in viewvalues(self.get_graph().get_parent_map(result_set)):
1334
for parents in self.get_graph().get_parent_map(
1335
result_set).itervalues():
2207
1336
result_parents.update(parents)
2208
1337
included_keys = result_set.intersection(result_parents)
2209
1338
start_keys = result_set.difference(included_keys)
2210
1339
exclude_keys = result_parents.difference(result_set)
2211
result = vf_search.SearchResult(start_keys, exclude_keys,
2212
len(result_set), result_set)
1340
result = graph.SearchResult(start_keys, exclude_keys,
1341
len(result_set), result_set)
2215
def search_missing_revision_ids(self, other,
2216
find_ghosts=True, revision_ids=None, if_present_ids=None,
1345
def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
2218
1346
"""Return the revision ids that other has that this does not.
2220
1348
These are returned in topological order.
2222
1350
revision_id: only return revision ids included by revision_id.
2224
with self.lock_read():
2225
inter_repo = _mod_repository.InterRepository.get(other, self)
2226
return inter_repo.search_missing_revision_ids(
2227
find_ghosts=find_ghosts, revision_ids=revision_ids,
2228
if_present_ids=if_present_ids, limit=limit)
1352
return repository.InterRepository.get(
1353
other, self).search_missing_revision_ids(revision_id, find_ghosts)
2230
def fetch(self, source, revision_id=None, find_ghosts=False,
2231
fetch_spec=None, lossy=False):
1355
def fetch(self, source, revision_id=None, pb=None, find_ghosts=False,
2232
1357
# No base implementation to use as RemoteRepository is not a subclass
2233
1358
# of Repository; so this is a copy of Repository.fetch().
2234
1359
if fetch_spec is not None and revision_id is not None:
2272
1397
return self._real_repository._get_versioned_file_checker(
2273
1398
revisions, revision_versions_cache)
2275
def _iter_files_bytes_rpc(self, desired_files, absent):
2276
path = self.controldir._path_for_remote_call(self._client)
2279
for (file_id, revid, identifier) in desired_files:
2280
lines.append(b''.join([
2281
osutils.safe_file_id(file_id),
2283
osutils.safe_revision_id(revid)]))
2284
identifiers.append(identifier)
2285
(response_tuple, response_handler) = (
2286
self._call_with_body_bytes_expecting_body(
2287
b"Repository.iter_files_bytes", (path, ), b"\n".join(lines)))
2288
if response_tuple != (b'ok', ):
2289
response_handler.cancel_read_body()
2290
raise errors.UnexpectedSmartServerResponse(response_tuple)
2291
byte_stream = response_handler.read_streamed_body()
2293
def decompress_stream(start, byte_stream, unused):
2294
decompressor = zlib.decompressobj()
2295
yield decompressor.decompress(start)
2296
while decompressor.unused_data == b"":
2298
data = next(byte_stream)
2299
except StopIteration:
2301
yield decompressor.decompress(data)
2302
yield decompressor.flush()
2303
unused.append(decompressor.unused_data)
2306
while b"\n" not in unused:
2308
unused += next(byte_stream)
2309
except StopIteration:
2311
header, rest = unused.split(b"\n", 1)
2312
args = header.split(b"\0")
2313
if args[0] == b"absent":
2314
absent[identifiers[int(args[3])]] = (args[1], args[2])
2317
elif args[0] == b"ok":
2320
raise errors.UnexpectedSmartServerResponse(args)
2322
yield (identifiers[idx],
2323
decompress_stream(rest, byte_stream, unused_chunks))
2324
unused = b"".join(unused_chunks)
2326
1400
def iter_files_bytes(self, desired_files):
2327
1401
"""See Repository.iter_file_bytes.
2331
for (identifier, bytes_iterator) in self._iter_files_bytes_rpc(
2332
desired_files, absent):
2333
yield identifier, bytes_iterator
2334
for fallback in self._fallback_repositories:
2337
desired_files = [(key[0], key[1], identifier)
2338
for identifier, key in viewitems(absent)]
2339
for (identifier, bytes_iterator) in fallback.iter_files_bytes(desired_files):
2340
del absent[identifier]
2341
yield identifier, bytes_iterator
2343
# There may be more missing items, but raise an exception
2345
missing_identifier = next(iter(absent))
2346
missing_key = absent[missing_identifier]
2347
raise errors.RevisionNotPresent(revision_id=missing_key[1],
2348
file_id=missing_key[0])
2349
except errors.UnknownSmartMethod:
2351
for (identifier, bytes_iterator) in (
2352
self._real_repository.iter_files_bytes(desired_files)):
2353
yield identifier, bytes_iterator
2355
def get_cached_parent_map(self, revision_ids):
2356
"""See breezy.CachingParentsProvider.get_cached_parent_map"""
2357
return self._unstacked_provider.get_cached_parent_map(revision_ids)
1404
return self._real_repository.iter_files_bytes(desired_files)
2359
1406
def get_parent_map(self, revision_ids):
2360
"""See breezy.Graph.get_parent_map()."""
1407
"""See bzrlib.Graph.get_parent_map()."""
2361
1408
return self._make_parents_provider().get_parent_map(revision_ids)
2363
1410
def _get_parent_map_rpc(self, keys):
2480
1538
revision_graph[d[0]] = (NULL_REVISION,)
2481
1539
return revision_graph
2483
1542
def get_signature_text(self, revision_id):
2484
with self.lock_read():
2485
path = self.controldir._path_for_remote_call(self._client)
2487
response_tuple, response_handler = self._call_expecting_body(
2488
b'Repository.get_revision_signature_text', path, revision_id)
2489
except errors.UnknownSmartMethod:
2491
return self._real_repository.get_signature_text(revision_id)
2492
except errors.NoSuchRevision as err:
2493
for fallback in self._fallback_repositories:
2495
return fallback.get_signature_text(revision_id)
2496
except errors.NoSuchRevision:
2500
if response_tuple[0] != b'ok':
2501
raise errors.UnexpectedSmartServerResponse(response_tuple)
2502
return response_handler.read_body_bytes()
1544
return self._real_repository.get_signature_text(revision_id)
2504
1547
def _get_inventory_xml(self, revision_id):
2505
with self.lock_read():
2506
# This call is used by older working tree formats,
2507
# which stored a serialized basis inventory.
2509
return self._real_repository._get_inventory_xml(revision_id)
1549
return self._real_repository._get_inventory_xml(revision_id)
2511
1551
def reconcile(self, other=None, thorough=False):
2512
from ..reconcile import ReconcileResult
2513
with self.lock_write():
2514
path = self.controldir._path_for_remote_call(self._client)
2516
response, handler = self._call_expecting_body(
2517
b'Repository.reconcile', path, self._lock_token)
2518
except (errors.UnknownSmartMethod, errors.TokenLockingNotSupported):
2520
return self._real_repository.reconcile(other=other, thorough=thorough)
2521
if response != (b'ok', ):
2522
raise errors.UnexpectedSmartServerResponse(response)
2523
body = handler.read_body_bytes()
2524
result = ReconcileResult()
2525
result.garbage_inventories = None
2526
result.inconsistent_parents = None
2527
result.aborted = None
2528
for line in body.split(b'\n'):
2531
key, val_text = line.split(b':')
2532
if key == b"garbage_inventories":
2533
result.garbage_inventories = int(val_text)
2534
elif key == b"inconsistent_parents":
2535
result.inconsistent_parents = int(val_text)
2537
mutter("unknown reconcile key %r" % key)
1553
return self._real_repository.reconcile(other=other, thorough=thorough)
2540
1555
def all_revision_ids(self):
2541
path = self.controldir._path_for_remote_call(self._client)
2543
response_tuple, response_handler = self._call_expecting_body(
2544
b"Repository.all_revision_ids", path)
2545
except errors.UnknownSmartMethod:
2547
return self._real_repository.all_revision_ids()
2548
if response_tuple != (b"ok", ):
2549
raise errors.UnexpectedSmartServerResponse(response_tuple)
2550
revids = set(response_handler.read_body_bytes().splitlines())
2551
for fallback in self._fallback_repositories:
2552
revids.update(set(fallback.all_revision_ids()))
2555
def _filtered_revision_trees(self, revision_ids, file_ids):
2556
"""Return Tree for a revision on this branch with only some files.
2558
:param revision_ids: a sequence of revision-ids;
2559
a revision-id may not be None or b'null:'
2560
:param file_ids: if not None, the result is filtered
2561
so that only those file-ids, their parents and their
2562
children are included.
2564
inventories = self.iter_inventories(revision_ids)
2565
for inv in inventories:
2566
# Should we introduce a FilteredRevisionTree class rather
2567
# than pre-filter the inventory here?
2568
filtered_inv = inv.filter(file_ids)
2569
yield InventoryRevisionTree(self, filtered_inv, filtered_inv.revision_id)
1557
return self._real_repository.all_revision_ids()
2571
1560
def get_deltas_for_revisions(self, revisions, specific_fileids=None):
2572
with self.lock_read():
2573
medium = self._client._medium
2574
if medium._is_remote_before((1, 2)):
2576
for delta in self._real_repository.get_deltas_for_revisions(
2577
revisions, specific_fileids):
2580
# Get the revision-ids of interest
2581
required_trees = set()
2582
for revision in revisions:
2583
required_trees.add(revision.revision_id)
2584
required_trees.update(revision.parent_ids[:1])
2586
# Get the matching filtered trees. Note that it's more
2587
# efficient to pass filtered trees to changes_from() rather
2588
# than doing the filtering afterwards. changes_from() could
2589
# arguably do the filtering itself but it's path-based, not
2590
# file-id based, so filtering before or afterwards is
2592
if specific_fileids is None:
2593
trees = dict((t.get_revision_id(), t) for
2594
t in self.revision_trees(required_trees))
2596
trees = dict((t.get_revision_id(), t) for
2597
t in self._filtered_revision_trees(required_trees,
2600
# Calculate the deltas
2601
for revision in revisions:
2602
if not revision.parent_ids:
2603
old_tree = self.revision_tree(_mod_revision.NULL_REVISION)
2605
old_tree = trees[revision.parent_ids[0]]
2606
yield trees[revision.revision_id].changes_from(old_tree)
2608
def get_revision_delta(self, revision_id):
2609
with self.lock_read():
2610
r = self.get_revision(revision_id)
2611
return list(self.get_deltas_for_revisions([r]))[0]
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)
2613
1572
def revision_trees(self, revision_ids):
2614
with self.lock_read():
2615
inventories = self.iter_inventories(revision_ids)
2616
for inv in inventories:
2617
yield RemoteInventoryTree(self, inv, inv.revision_id)
1574
return self._real_repository.revision_trees(revision_ids)
2619
1577
def get_revision_reconcile(self, revision_id):
2620
with self.lock_read():
2622
return self._real_repository.get_revision_reconcile(revision_id)
1579
return self._real_repository.get_revision_reconcile(revision_id)
2624
1582
def check(self, revision_ids=None, callback_refs=None, check_repo=True):
2625
with self.lock_read():
2627
return self._real_repository.check(revision_ids=revision_ids,
2628
callback_refs=callback_refs, check_repo=check_repo)
1584
return self._real_repository.check(revision_ids=revision_ids,
1585
callback_refs=callback_refs, check_repo=check_repo)
2630
1587
def copy_content_into(self, destination, revision_id=None):
2631
"""Make a complete copy of the content in self into destination.
2633
This is a destructive operation! Do not use it on existing
2636
interrepo = _mod_repository.InterRepository.get(self, destination)
2637
return interrepo.copy_content(revision_id)
1589
return self._real_repository.copy_content_into(
1590
destination, revision_id=revision_id)
2639
1592
def _copy_repository_tarball(self, to_bzrdir, revision_id=None):
2640
1593
# get a tarball of the remote repository, and copy from that into the
1595
from bzrlib import osutils
2643
1597
# TODO: Maybe a progress bar while streaming the tarball?
2644
note(gettext("Copying repository content as tarball..."))
1598
note("Copying repository content as tarball...")
2645
1599
tar_file = self._get_tarball('bz2')
2646
1600
if tar_file is None:
2648
1602
destination = to_bzrdir.create_repository()
2650
1604
tar = tarfile.open('repository', fileobj=tar_file,
2652
1606
tmpdir = osutils.mkdtemp()
2654
tar.extractall(tmpdir)
2655
tmp_bzrdir = _mod_bzrdir.BzrDir.open(tmpdir)
1608
_extract_tar(tar, tmpdir)
1609
tmp_bzrdir = BzrDir.open(tmpdir)
2656
1610
tmp_repo = tmp_bzrdir.open_repository()
2657
1611
tmp_repo.copy_content_into(destination, revision_id)
2746
1689
self._ensure_real()
2747
1690
return self._real_repository.texts
2749
def _iter_revisions_rpc(self, revision_ids):
2750
body = b"\n".join(revision_ids)
2751
path = self.controldir._path_for_remote_call(self._client)
2752
response_tuple, response_handler = (
2753
self._call_with_body_bytes_expecting_body(
2754
b"Repository.iter_revisions", (path, ), body))
2755
if response_tuple[0] != b"ok":
2756
raise errors.UnexpectedSmartServerResponse(response_tuple)
2757
serializer_format = response_tuple[1].decode('ascii')
2758
serializer = serializer_format_registry.get(serializer_format)
2759
byte_stream = response_handler.read_streamed_body()
2760
decompressor = zlib.decompressobj()
2762
for bytes in byte_stream:
2763
chunks.append(decompressor.decompress(bytes))
2764
if decompressor.unused_data != b"":
2765
chunks.append(decompressor.flush())
2766
yield serializer.read_revision_from_string(b"".join(chunks))
2767
unused = decompressor.unused_data
2768
decompressor = zlib.decompressobj()
2769
chunks = [decompressor.decompress(unused)]
2770
chunks.append(decompressor.flush())
2771
text = b"".join(chunks)
2773
yield serializer.read_revision_from_string(b"".join(chunks))
2775
def iter_revisions(self, revision_ids):
2776
for rev_id in revision_ids:
2777
if not rev_id or not isinstance(rev_id, bytes):
2778
raise errors.InvalidRevisionId(
2779
revision_id=rev_id, branch=self)
2780
with self.lock_read():
2782
missing = set(revision_ids)
2783
for rev in self._iter_revisions_rpc(revision_ids):
2784
missing.remove(rev.revision_id)
2785
yield (rev.revision_id, rev)
2786
for fallback in self._fallback_repositories:
2789
for (revid, rev) in fallback.iter_revisions(missing):
2792
missing.remove(revid)
2793
for revid in missing:
2795
except errors.UnknownSmartMethod:
2797
for entry in self._real_repository.iter_revisions(revision_ids):
1693
def get_revisions(self, revision_ids):
1695
return self._real_repository.get_revisions(revision_ids)
2800
1697
def supports_rich_root(self):
2801
1698
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)
2804
1705
def _serializer(self):
2805
1706
return self._format._serializer
2807
1708
def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
2808
with self.lock_write():
2809
signature = gpg_strategy.sign(plaintext, gpg.MODE_CLEAR)
2810
self.add_signature_text(revision_id, signature)
1710
return self._real_repository.store_revision_signature(
1711
gpg_strategy, plaintext, revision_id)
2812
1713
def add_signature_text(self, revision_id, signature):
2813
if self._real_repository:
2814
# If there is a real repository the write group will
2815
# be in the real repository as well, so use that:
2817
return self._real_repository.add_signature_text(
2818
revision_id, signature)
2819
path = self.controldir._path_for_remote_call(self._client)
2820
response, handler = self._call_with_body_bytes_expecting_body(
2821
b'Repository.add_signature_text', (path, self._lock_token,
2823
tuple([token.encode('utf-8')
2824
for token in self._write_group_tokens]),
2826
handler.cancel_read_body()
2828
if response[0] != b'ok':
2829
raise errors.UnexpectedSmartServerResponse(response)
2830
self._write_group_tokens = [token.decode(
2831
'utf-8') for token in response[1:]]
1715
return self._real_repository.add_signature_text(revision_id, signature)
2833
1717
def has_signature_for_revision_id(self, revision_id):
2834
path = self.controldir._path_for_remote_call(self._client)
2836
response = self._call(b'Repository.has_signature_for_revision_id',
2838
except errors.UnknownSmartMethod:
2840
return self._real_repository.has_signature_for_revision_id(
2842
if response[0] not in (b'yes', b'no'):
2843
raise SmartProtocolError(
2844
'unexpected response code %s' % (response,))
2845
if response[0] == b'yes':
2847
for fallback in self._fallback_repositories:
2848
if fallback.has_signature_for_revision_id(revision_id):
2852
def verify_revision_signature(self, revision_id, gpg_strategy):
2853
with self.lock_read():
2854
if not self.has_signature_for_revision_id(revision_id):
2855
return gpg.SIGNATURE_NOT_SIGNED, None
2856
signature = self.get_signature_text(revision_id)
2858
testament = _mod_testament.Testament.from_revision(
2861
(status, key, signed_plaintext) = gpg_strategy.verify(signature)
2862
if testament.as_short_text() != signed_plaintext:
2863
return gpg.SIGNATURE_NOT_VALID, None
2864
return (status, key)
1719
return self._real_repository.has_signature_for_revision_id(revision_id)
2866
1721
def item_keys_introduced_by(self, revision_ids, _files_pb=None):
2867
1722
self._ensure_real()
2868
1723
return self._real_repository.item_keys_introduced_by(revision_ids,
2869
_files_pb=_files_pb)
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()
2871
1731
def _find_inconsistent_revision_parents(self, revisions_iterator=None):
2872
1732
self._ensure_real()
2890
1751
:param recipe: A search recipe (start, stop, count).
2891
1752
:return: Serialised bytes.
2893
start_keys = b' '.join(recipe[1])
2894
stop_keys = b' '.join(recipe[2])
2895
count = str(recipe[3]).encode('ascii')
2896
return b'\n'.join((start_keys, stop_keys, count))
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))
2898
1759
def _serialise_search_result(self, search_result):
2899
parts = search_result.get_network_struct()
2900
return b'\n'.join(parts)
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)
2902
1768
def autopack(self):
2903
path = self.controldir._path_for_remote_call(self._client)
1769
path = self.bzrdir._path_for_remote_call(self._client)
2905
response = self._call(b'PackRepository.autopack', path)
1771
response = self._call('PackRepository.autopack', path)
2906
1772
except errors.UnknownSmartMethod:
2907
1773
self._ensure_real()
2908
1774
self._real_repository._pack_collection.autopack()
2910
1776
self.refresh_data()
2911
if response[0] != b'ok':
2912
raise errors.UnexpectedSmartServerResponse(response)
2914
def _revision_archive(self, revision_id, format, name, root, subdir,
2916
path = self.controldir._path_for_remote_call(self._client)
2917
format = format or ''
2919
subdir = subdir or ''
2920
force_mtime = int(force_mtime) if force_mtime is not None else None
2922
response, protocol = self._call_expecting_body(
2923
b'Repository.revision_archive', path,
2925
format.encode('ascii'),
2926
os.path.basename(name).encode('utf-8'),
2927
root.encode('utf-8'),
2928
subdir.encode('utf-8'),
2930
except errors.UnknownSmartMethod:
2932
if response[0] == b'ok':
2933
return iter([protocol.read_body_bytes()])
2934
raise errors.UnexpectedSmartServerResponse(response)
2936
def _annotate_file_revision(self, revid, tree_path, file_id, default_revision):
2937
path = self.controldir._path_for_remote_call(self._client)
2938
tree_path = tree_path.encode('utf-8')
2939
file_id = file_id or b''
2940
default_revision = default_revision or b''
2942
response, handler = self._call_expecting_body(
2943
b'Repository.annotate_file_revision', path,
2944
revid, tree_path, file_id, default_revision)
2945
except errors.UnknownSmartMethod:
2947
if response[0] != b'ok':
2948
raise errors.UnexpectedSmartServerResponse(response)
2949
return map(tuple, bencode.bdecode(handler.read_body_bytes()))
2952
class RemoteStreamSink(vf_repository.StreamSink):
1777
if response[0] != 'ok':
1778
raise errors.UnexpectedSmartServerResponse(response)
1781
class RemoteStreamSink(repository.StreamSink):
2954
1783
def _insert_real(self, stream, src_format, resume_tokens):
2955
1784
self.target_repo._ensure_real()
3299
2069
def network_name(self):
3300
2070
return self._network_name
3302
def open(self, a_controldir, name=None, ignore_fallbacks=False):
3303
return a_controldir.open_branch(name=name,
3304
ignore_fallbacks=ignore_fallbacks)
2072
def open(self, a_bzrdir, name=None, ignore_fallbacks=False):
2073
return a_bzrdir.open_branch(name=name,
2074
ignore_fallbacks=ignore_fallbacks)
3306
def _vfs_initialize(self, a_controldir, name, append_revisions_only,
2076
def _vfs_initialize(self, a_bzrdir, name):
3308
2077
# Initialisation when using a local bzrdir object, or a non-vfs init
3309
2078
# method is not available on the server.
3310
2079
# self._custom_format is always set - the start of initialize ensures
3312
if isinstance(a_controldir, RemoteBzrDir):
3313
a_controldir._ensure_real()
3314
result = self._custom_format.initialize(a_controldir._real_bzrdir,
3315
name=name, append_revisions_only=append_revisions_only,
3316
repository=repository)
2081
if isinstance(a_bzrdir, RemoteBzrDir):
2082
a_bzrdir._ensure_real()
2083
result = self._custom_format.initialize(a_bzrdir._real_bzrdir,
3318
2086
# We assume the bzrdir is parameterised; it may not be.
3319
result = self._custom_format.initialize(a_controldir, name=name,
3320
append_revisions_only=append_revisions_only,
3321
repository=repository)
3322
if (isinstance(a_controldir, RemoteBzrDir)
3323
and not isinstance(result, RemoteBranch)):
3324
result = RemoteBranch(a_controldir, a_controldir.find_repository(), result,
2087
result = self._custom_format.initialize(a_bzrdir, name)
2088
if (isinstance(a_bzrdir, RemoteBzrDir) and
2089
not isinstance(result, RemoteBranch)):
2090
result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result,
3328
def initialize(self, a_controldir, name=None, repository=None,
3329
append_revisions_only=None):
3331
name = a_controldir._get_selected_branch()
2094
def initialize(self, a_bzrdir, name=None):
3332
2095
# 1) get the network name to use.
3333
2096
if self._custom_format:
3334
2097
network_name = self._custom_format.network_name()
3336
# Select the current breezy default and ask for that.
3337
reference_bzrdir_format = controldir.format_registry.get(
2099
# Select the current bzrlib default and ask for that.
2100
reference_bzrdir_format = bzrdir.format_registry.get('default')()
3339
2101
reference_format = reference_bzrdir_format.get_branch_format()
3340
2102
self._custom_format = reference_format
3341
2103
network_name = reference_format.network_name()
3342
2104
# Being asked to create on a non RemoteBzrDir:
3343
if not isinstance(a_controldir, RemoteBzrDir):
3344
return self._vfs_initialize(a_controldir, name=name,
3345
append_revisions_only=append_revisions_only,
3346
repository=repository)
3347
medium = a_controldir._client._medium
2105
if not isinstance(a_bzrdir, RemoteBzrDir):
2106
return self._vfs_initialize(a_bzrdir, name=name)
2107
medium = a_bzrdir._client._medium
3348
2108
if medium._is_remote_before((1, 13)):
3349
return self._vfs_initialize(a_controldir, name=name,
3350
append_revisions_only=append_revisions_only,
3351
repository=repository)
2109
return self._vfs_initialize(a_bzrdir, name=name)
3352
2110
# Creating on a remote bzr dir.
3353
2111
# 2) try direct creation via RPC
3354
path = a_controldir._path_for_remote_call(a_controldir._client)
2112
path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
2113
if name is not None:
3356
2114
# XXX JRV20100304: Support creating colocated branches
3357
2115
raise errors.NoColocatedBranchSupport(self)
3358
verb = b'BzrDir.create_branch'
2116
verb = 'BzrDir.create_branch'
3360
response = a_controldir._call(verb, path, network_name)
2118
response = a_bzrdir._call(verb, path, network_name)
3361
2119
except errors.UnknownSmartMethod:
3362
2120
# Fallback - use vfs methods
3363
2121
medium._remember_remote_is_before((1, 13))
3364
return self._vfs_initialize(a_controldir, name=name,
3365
append_revisions_only=append_revisions_only,
3366
repository=repository)
3367
if response[0] != b'ok':
2122
return self._vfs_initialize(a_bzrdir, name=name)
2123
if response[0] != 'ok':
3368
2124
raise errors.UnexpectedSmartServerResponse(response)
3369
2125
# Turn the response into a RemoteRepository object.
3370
2126
format = RemoteBranchFormat(network_name=response[1])
3371
2127
repo_format = response_tuple_to_repo_format(response[3:])
3372
repo_path = response[2].decode('utf-8')
3373
if repository is not None:
3374
remote_repo_url = urlutils.join(a_controldir.user_url, repo_path)
3375
url_diff = urlutils.relative_url(repository.user_url,
3378
raise AssertionError(
3379
'repository.user_url %r does not match URL from server '
3380
'response (%r + %r)'
3381
% (repository.user_url, a_controldir.user_url, repo_path))
3382
remote_repo = repository
2128
if response[2] == '':
2129
repo_bzrdir = a_bzrdir
3385
repo_bzrdir = a_controldir
3387
repo_bzrdir = RemoteBzrDir(
3388
a_controldir.root_transport.clone(
3389
repo_path), a_controldir._format,
3390
a_controldir._client)
3391
remote_repo = RemoteRepository(repo_bzrdir, repo_format)
3392
remote_branch = RemoteBranch(a_controldir, remote_repo,
3393
format=format, setup_stacking=False, name=name)
3394
if append_revisions_only:
3395
remote_branch.set_append_revisions_only(append_revisions_only)
2131
repo_bzrdir = RemoteBzrDir(
2132
a_bzrdir.root_transport.clone(response[2]), a_bzrdir._format,
2134
remote_repo = RemoteRepository(repo_bzrdir, repo_format)
2135
remote_branch = RemoteBranch(a_bzrdir, remote_repo,
2136
format=format, setup_stacking=False, name=name)
3396
2137
# XXX: We know this is a new branch, so it must have revno 0, revid
3397
2138
# NULL_REVISION. Creating the branch locked would make this be unable
3398
2139
# to be wrong; here its simply very unlikely to be wrong. RBC 20090225
3417
2158
self._ensure_real()
3418
2159
return self._custom_format.supports_set_append_revisions_only()
3421
def supports_reference_locations(self):
3423
return self._custom_format.supports_reference_locations
3425
def stores_revno(self):
3428
def _use_default_local_heads_to_fetch(self):
3429
# If the branch format is a metadir format *and* its heads_to_fetch
3430
# implementation is not overridden vs the base class, we can use the
3431
# base class logic rather than use the heads_to_fetch RPC. This is
3432
# usually cheaper in terms of net round trips, as the last-revision and
3433
# tags info fetched is cached and would be fetched anyway.
3435
if isinstance(self._custom_format, bzrbranch.BranchFormatMetadir):
3436
branch_class = self._custom_format._branch_class()
3437
heads_to_fetch_impl = get_unbound_function(
3438
branch_class.heads_to_fetch)
3439
if heads_to_fetch_impl is get_unbound_function(branch.Branch.heads_to_fetch):
3444
class RemoteBranchStore(_mod_config.IniFileStore):
3445
"""Branch store which attempts to use HPSS calls to retrieve branch store.
3447
Note that this is specific to bzr-based formats.
3450
def __init__(self, branch):
3451
super(RemoteBranchStore, self).__init__()
3452
self.branch = branch
3454
self._real_store = None
3456
def external_url(self):
3457
return urlutils.join(self.branch.user_url, 'branch.conf')
3459
def _load_content(self):
3460
path = self.branch._remote_path()
3462
response, handler = self.branch._call_expecting_body(
3463
b'Branch.get_config_file', path)
3464
except errors.UnknownSmartMethod:
3466
return self._real_store._load_content()
3467
if len(response) and response[0] != b'ok':
3468
raise errors.UnexpectedSmartServerResponse(response)
3469
return handler.read_body_bytes()
3471
def _save_content(self, content):
3472
path = self.branch._remote_path()
3474
response, handler = self.branch._call_with_body_bytes_expecting_body(
3475
b'Branch.put_config_file', (path,
3476
self.branch._lock_token, self.branch._repo_lock_token),
3478
except errors.UnknownSmartMethod:
3480
return self._real_store._save_content(content)
3481
handler.cancel_read_body()
3482
if response != (b'ok', ):
3483
raise errors.UnexpectedSmartServerResponse(response)
3485
def _ensure_real(self):
3486
self.branch._ensure_real()
3487
if self._real_store is None:
3488
self._real_store = _mod_config.BranchStore(self.branch)
3491
2162
class RemoteBranch(branch.Branch, _RpcHelper, lock._RelockDebugMixin):
3492
2163
"""Branch stored on a server accessed by HPSS RPC.
4043
2666
self._ensure_real()
4044
2667
return self._real_branch._set_parent_location(url)
4046
2670
def pull(self, source, overwrite=False, stop_revision=None,
4048
with self.lock_write():
4049
self._clear_cached_state_of_remote_branch_only()
4051
return self._real_branch.pull(
4052
source, overwrite=overwrite, stop_revision=stop_revision,
4053
_override_hook_target=self, **kwargs)
4055
def push(self, target, overwrite=False, stop_revision=None, lossy=False, tag_selector=None):
4056
with self.lock_read():
4058
return self._real_branch.push(
4059
target, overwrite=overwrite, stop_revision=stop_revision, lossy=lossy,
4060
_override_hook_source_branch=self, tag_selector=tag_selector)
4062
def peek_lock_mode(self):
4063
return self._lock_mode
2672
self._clear_cached_state_of_remote_branch_only()
2674
return self._real_branch.pull(
2675
source, overwrite=overwrite, stop_revision=stop_revision,
2676
_override_hook_target=self, **kwargs)
2679
def push(self, target, overwrite=False, stop_revision=None):
2681
return self._real_branch.push(
2682
target, overwrite=overwrite, stop_revision=stop_revision,
2683
_override_hook_source_branch=self)
4065
2685
def is_locked(self):
4066
2686
return self._lock_count >= 1
4068
def revision_id_to_dotted_revno(self, revision_id):
4069
"""Given a revision id, return its dotted revno.
4071
:return: a tuple like (1,) or (400,1,3).
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_dotted_revno(revision_id)
4080
except errors.UnknownErrorFromSmartServer as e:
4081
# Deal with older versions of bzr/brz that didn't explicitly
4082
# wrap GhostRevisionsHaveNoRevno.
4083
if e.error_tuple[1] == b'GhostRevisionsHaveNoRevno':
4084
(revid, ghost_revid) = re.findall(b"{([^}]+)}", e.error_tuple[2])
4085
raise errors.GhostRevisionsHaveNoRevno(
4088
if response[0] == b'ok':
4089
return tuple([int(x) for x in response[1:]])
4091
raise errors.UnexpectedSmartServerResponse(response)
4093
2689
def revision_id_to_revno(self, revision_id):
4094
"""Given a revision id on the branch mainline, return its revno.
4098
with self.lock_read():
4100
response = self._call(b'Branch.revision_id_to_revno',
4101
self._remote_path(), revision_id)
4102
except errors.UnknownSmartMethod:
4104
return self._real_branch.revision_id_to_revno(revision_id)
4105
if response[0] == b'ok':
4106
if len(response) == 2:
4107
return int(response[1])
4108
raise NoSuchRevision(self, revision_id)
4110
raise errors.UnexpectedSmartServerResponse(response)
2691
return self._real_branch.revision_id_to_revno(revision_id)
4112
2694
def set_last_revision_info(self, revno, revision_id):
4113
with self.lock_write():
4114
# XXX: These should be returned by the set_last_revision_info verb
4115
old_revno, old_revid = self.last_revision_info()
4116
self._run_pre_change_branch_tip_hooks(revno, revision_id)
4117
if not revision_id or not isinstance(revision_id, bytes):
4118
raise errors.InvalidRevisionId(
4119
revision_id=revision_id, branch=self)
4121
response = self._call(b'Branch.set_last_revision_info',
4122
self._remote_path(), self._lock_token, self._repo_lock_token,
4123
str(revno).encode('ascii'), revision_id)
4124
except errors.UnknownSmartMethod:
4126
self._clear_cached_state_of_remote_branch_only()
4127
self._real_branch.set_last_revision_info(revno, revision_id)
4128
self._last_revision_info_cache = revno, revision_id
4130
if response == (b'ok',):
4131
self._clear_cached_state()
4132
self._last_revision_info_cache = revno, revision_id
4133
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
4134
# Update the _real_branch's cache too.
4135
if self._real_branch is not None:
4136
cache = self._last_revision_info_cache
4137
self._real_branch._last_revision_info_cache = cache
4139
raise errors.UnexpectedSmartServerResponse(response)
2695
# XXX: These should be returned by the set_last_revision_info verb
2696
old_revno, old_revid = self.last_revision_info()
2697
self._run_pre_change_branch_tip_hooks(revno, revision_id)
2698
revision_id = ensure_null(revision_id)
2700
response = self._call('Branch.set_last_revision_info',
2701
self._remote_path(), self._lock_token, self._repo_lock_token,
2702
str(revno), revision_id)
2703
except errors.UnknownSmartMethod:
2705
self._clear_cached_state_of_remote_branch_only()
2706
self._real_branch.set_last_revision_info(revno, revision_id)
2707
self._last_revision_info_cache = revno, revision_id
2709
if response == ('ok',):
2710
self._clear_cached_state()
2711
self._last_revision_info_cache = revno, revision_id
2712
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2713
# Update the _real_branch's cache too.
2714
if self._real_branch is not None:
2715
cache = self._last_revision_info_cache
2716
self._real_branch._last_revision_info_cache = cache
2718
raise errors.UnexpectedSmartServerResponse(response)
4141
2721
def generate_revision_history(self, revision_id, last_rev=None,
4142
2722
other_branch=None):
4143
with self.lock_write():
4144
medium = self._client._medium
4145
if not medium._is_remote_before((1, 6)):
4146
# Use a smart method for 1.6 and above servers
4148
self._set_last_revision_descendant(revision_id, other_branch,
4149
allow_diverged=True, allow_overwrite_descendant=True)
4151
except errors.UnknownSmartMethod:
4152
medium._remember_remote_is_before((1, 6))
4153
self._clear_cached_state_of_remote_branch_only()
4154
graph = self.repository.get_graph()
4155
(last_revno, last_revid) = self.last_revision_info()
4156
known_revision_ids = [
4157
(last_revid, last_revno),
4158
(_mod_revision.NULL_REVISION, 0),
4160
if last_rev is not None:
4161
if not graph.is_ancestor(last_rev, revision_id):
4162
# our previous tip is not merged into stop_revision
4163
raise errors.DivergedBranches(self, other_branch)
4164
revno = graph.find_distance_to_null(
4165
revision_id, known_revision_ids)
4166
self.set_last_revision_info(revno, revision_id)
2723
medium = self._client._medium
2724
if not medium._is_remote_before((1, 6)):
2725
# Use a smart method for 1.6 and above servers
2727
self._set_last_revision_descendant(revision_id, other_branch,
2728
allow_diverged=True, allow_overwrite_descendant=True)
2730
except errors.UnknownSmartMethod:
2731
medium._remember_remote_is_before((1, 6))
2732
self._clear_cached_state_of_remote_branch_only()
2733
self.set_revision_history(self._lefthand_history(revision_id,
2734
last_rev=last_rev,other_branch=other_branch))
4168
2736
def set_push_location(self, location):
4169
self._set_config_location('push_location', location)
4171
def heads_to_fetch(self):
4172
if self._format._use_default_local_heads_to_fetch():
4173
# We recognise this format, and its heads-to-fetch implementation
4174
# is the default one (tip + tags). In this case it's cheaper to
4175
# just use the default implementation rather than a special RPC as
4176
# the tip and tags data is cached.
4177
return branch.Branch.heads_to_fetch(self)
4178
medium = self._client._medium
4179
if medium._is_remote_before((2, 4)):
4180
return self._vfs_heads_to_fetch()
4182
return self._rpc_heads_to_fetch()
4183
except errors.UnknownSmartMethod:
4184
medium._remember_remote_is_before((2, 4))
4185
return self._vfs_heads_to_fetch()
4187
def _rpc_heads_to_fetch(self):
4188
response = self._call(b'Branch.heads_to_fetch', self._remote_path())
4189
if len(response) != 2:
4190
raise errors.UnexpectedSmartServerResponse(response)
4191
must_fetch, if_present_fetch = response
4192
return set(must_fetch), set(if_present_fetch)
4194
def _vfs_heads_to_fetch(self):
4196
return self._real_branch.heads_to_fetch()
4198
def reconcile(self, thorough=True):
4199
"""Make sure the data stored in this branch is consistent."""
4200
from .reconcile import BranchReconciler
4201
with self.lock_write():
4202
reconciler = BranchReconciler(self, thorough=thorough)
4203
return reconciler.reconcile()
4205
def get_reference_info(self, file_id):
4206
"""Get the tree_path and branch_location for a tree reference."""
4207
if not self._format.supports_reference_locations:
4208
raise errors.UnsupportedOperation(self.get_reference_info, self)
4209
return self._get_all_reference_info().get(file_id, (None, None))
4211
def set_reference_info(self, file_id, branch_location, tree_path=None):
4212
"""Set the branch location to use for a tree reference."""
4213
if not self._format.supports_reference_locations:
4214
raise errors.UnsupportedOperation(self.set_reference_info, self)
4216
self._real_branch.set_reference_info(
4217
file_id, branch_location, tree_path)
4219
def _set_all_reference_info(self, reference_info):
4220
if not self._format.supports_reference_locations:
4221
raise errors.UnsupportedOperation(self.set_reference_info, self)
4223
self._real_branch._set_all_reference_info(reference_info)
4225
def _get_all_reference_info(self):
4226
if not self._format.supports_reference_locations:
4229
response, handler = self._call_expecting_body(
4230
b'Branch.get_all_reference_info', self._remote_path())
4231
except errors.UnknownSmartMethod:
4233
return self._real_branch._get_all_reference_info()
4234
if len(response) and response[0] != b'ok':
4235
raise errors.UnexpectedSmartServerResponse(response)
4237
for (f, u, p) in bencode.bdecode(handler.read_body_bytes()):
4238
ret[f] = (u.decode('utf-8'), p.decode('utf-8') if p else None)
4241
def reference_parent(self, file_id, path, possible_transports=None):
4242
"""Return the parent branch for a tree-reference.
4244
:param path: The path of the nested tree in the tree
4245
:return: A branch associated with the nested tree
4247
branch_location = self.get_reference_info(file_id)[0]
4248
if branch_location is None:
4250
return branch.Branch.open_from_transport(
4251
self.controldir.root_transport.clone(path),
4252
possible_transports=possible_transports)
4253
except errors.NotBranchError:
4255
return branch.Branch.open(
4257
urlutils.strip_segment_parameters(self.user_url), branch_location),
4258
possible_transports=possible_transports)
2738
return self._real_branch.set_push_location(location)
4261
2741
class RemoteConfig(object):
4443
2912
def find(name):
4445
2914
return context[name]
4447
mutter('Missing key \'%s\' in context %r', name, context)
2915
except KeyError, key_err:
2916
mutter('Missing key %r in context %r', key_err.args[0], context)
4450
2918
def get_path():
4451
2919
"""Get the path from the context if present, otherwise use first error
4455
2923
return context['path']
2924
except KeyError, key_err:
4458
return err.error_args[0].decode('utf-8')
4460
mutter('Missing key \'path\' in context %r', context)
2926
return err.error_args[0]
2927
except IndexError, idx_err:
2929
'Missing key %r in context %r', key_err.args[0], context)
4462
if not isinstance(err.error_verb, bytes):
4463
raise TypeError(err.error_verb)
4465
translator = error_translators.get(err.error_verb)
4469
raise translator(err, find, get_path)
4471
translator = no_context_error_translators.get(err.error_verb)
4473
raise errors.UnknownErrorFromSmartServer(err)
4475
raise translator(err)
4478
error_translators.register(b'NoSuchRevision',
4479
lambda err, find, get_path: NoSuchRevision(
4480
find('branch'), err.error_args[0]))
4481
error_translators.register(b'nosuchrevision',
4482
lambda err, find, get_path: NoSuchRevision(
4483
find('repository'), err.error_args[0]))
4484
error_translators.register(
4485
b'revno-outofbounds',
4486
lambda err, find, get_path: errors.RevnoOutOfBounds(
4487
err.error_args[0], (err.error_args[1], err.error_args[2])))
4490
def _translate_nobranch_error(err, find, get_path):
4491
if len(err.error_args) >= 1:
4492
extra = err.error_args[0].decode('utf-8')
4495
return errors.NotBranchError(path=find('bzrdir').root_transport.base,
4499
error_translators.register(b'nobranch', _translate_nobranch_error)
4500
error_translators.register(b'norepository',
4501
lambda err, find, get_path: errors.NoRepositoryPresent(
4503
error_translators.register(b'UnlockableTransport',
4504
lambda err, find, get_path: errors.UnlockableTransport(
4505
find('bzrdir').root_transport))
4506
error_translators.register(b'TokenMismatch',
4507
lambda err, find, get_path: errors.TokenMismatch(
4508
find('token'), '(remote token)'))
4509
error_translators.register(b'Diverged',
4510
lambda err, find, get_path: errors.DivergedBranches(
4511
find('branch'), find('other_branch')))
4512
error_translators.register(b'NotStacked',
4513
lambda err, find, get_path: errors.NotStacked(branch=find('branch')))
4516
def _translate_PermissionDenied(err, find, get_path):
4518
if len(err.error_args) >= 2:
4519
extra = err.error_args[1].decode('utf-8')
4522
return errors.PermissionDenied(path, extra=extra)
4525
error_translators.register(b'PermissionDenied', _translate_PermissionDenied)
4526
error_translators.register(b'ReadError',
4527
lambda err, find, get_path: errors.ReadError(get_path()))
4528
error_translators.register(b'NoSuchFile',
4529
lambda err, find, get_path: errors.NoSuchFile(get_path()))
4530
error_translators.register(b'TokenLockingNotSupported',
4531
lambda err, find, get_path: errors.TokenLockingNotSupported(
4532
find('repository')))
4533
error_translators.register(b'UnsuspendableWriteGroup',
4534
lambda err, find, get_path: errors.UnsuspendableWriteGroup(
4535
repository=find('repository')))
4536
error_translators.register(b'UnresumableWriteGroup',
4537
lambda err, find, get_path: errors.UnresumableWriteGroup(
4538
repository=find('repository'), write_groups=err.error_args[0],
4539
reason=err.error_args[1]))
4540
no_context_error_translators.register(b'GhostRevisionsHaveNoRevno',
4541
lambda err: errors.GhostRevisionsHaveNoRevno(*err.error_args))
4542
no_context_error_translators.register(b'IncompatibleRepositories',
4543
lambda err: errors.IncompatibleRepositories(
4544
err.error_args[0].decode('utf-8'), err.error_args[1].decode('utf-8'), err.error_args[2].decode('utf-8')))
4545
no_context_error_translators.register(b'LockContention',
4546
lambda err: errors.LockContention('(remote lock)'))
4547
no_context_error_translators.register(b'LockFailed',
4548
lambda err: errors.LockFailed(err.error_args[0].decode('utf-8'), err.error_args[1].decode('utf-8')))
4549
no_context_error_translators.register(b'TipChangeRejected',
4550
lambda err: errors.TipChangeRejected(err.error_args[0].decode('utf8')))
4551
no_context_error_translators.register(b'UnstackableBranchFormat',
4552
lambda err: branch.UnstackableBranchFormat(*err.error_args))
4553
no_context_error_translators.register(b'UnstackableRepositoryFormat',
4554
lambda err: errors.UnstackableRepositoryFormat(*err.error_args))
4555
no_context_error_translators.register(b'FileExists',
4556
lambda err: errors.FileExists(err.error_args[0].decode('utf-8')))
4557
no_context_error_translators.register(b'DirectoryNotEmpty',
4558
lambda err: errors.DirectoryNotEmpty(err.error_args[0].decode('utf-8')))
4559
no_context_error_translators.register(b'UnknownFormat',
4560
lambda err: errors.UnknownFormatError(
4561
err.error_args[0].decode('ascii'), err.error_args[0].decode('ascii')))
4562
no_context_error_translators.register(b'InvalidURL',
4563
lambda err: urlutils.InvalidURL(
4564
err.error_args[0].decode('utf-8'), err.error_args[1].decode('ascii')))
4567
def _translate_short_readv_error(err):
4568
args = err.error_args
4569
return errors.ShortReadvError(
4570
args[0].decode('utf-8'),
4571
int(args[1].decode('ascii')), int(args[2].decode('ascii')),
4572
int(args[3].decode('ascii')))
4575
no_context_error_translators.register(b'ShortReadvError',
4576
_translate_short_readv_error)
4579
def _translate_unicode_error(err):
4580
encoding = err.error_args[0].decode('ascii')
4581
val = err.error_args[1].decode('utf-8')
4582
start = int(err.error_args[2].decode('ascii'))
4583
end = int(err.error_args[3].decode('ascii'))
4584
reason = err.error_args[4].decode('utf-8')
4585
if val.startswith('u:'):
4586
val = val[2:].decode('utf-8')
4587
elif val.startswith('s:'):
4588
val = val[2:].decode('base64')
4589
if err.error_verb == 'UnicodeDecodeError':
4590
raise UnicodeDecodeError(encoding, val, start, end, reason)
4591
elif err.error_verb == 'UnicodeEncodeError':
4592
raise UnicodeEncodeError(encoding, val, start, end, reason)
4595
no_context_error_translators.register(b'UnicodeEncodeError',
4596
_translate_unicode_error)
4597
no_context_error_translators.register(b'UnicodeDecodeError',
4598
_translate_unicode_error)
4599
no_context_error_translators.register(b'ReadOnlyError',
4600
lambda err: errors.TransportNotPossible('readonly transport'))
4601
no_context_error_translators.register(b'MemoryError',
4602
lambda err: errors.BzrError("remote server out of memory\n"
4603
"Retry non-remotely, or contact the server admin for details."))
4604
no_context_error_translators.register(b'RevisionNotPresent',
4605
lambda err: errors.RevisionNotPresent(err.error_args[0].decode('utf-8'), err.error_args[1].decode('utf-8')))
4607
no_context_error_translators.register(b'BzrCheckError',
4608
lambda err: errors.BzrCheckError(msg=err.error_args[0].decode('utf-8')))
2932
if err.error_verb == 'IncompatibleRepositories':
2933
raise errors.IncompatibleRepositories(err.error_args[0],
2934
err.error_args[1], err.error_args[2])
2935
elif err.error_verb == 'NoSuchRevision':
2936
raise NoSuchRevision(find('branch'), err.error_args[0])
2937
elif err.error_verb == 'nosuchrevision':
2938
raise NoSuchRevision(find('repository'), err.error_args[0])
2939
elif err.error_verb == 'nobranch':
2940
if len(err.error_args) >= 1:
2941
extra = err.error_args[0]
2944
raise errors.NotBranchError(path=find('bzrdir').root_transport.base,
2946
elif err.error_verb == 'norepository':
2947
raise errors.NoRepositoryPresent(find('bzrdir'))
2948
elif err.error_verb == 'LockContention':
2949
raise errors.LockContention('(remote lock)')
2950
elif err.error_verb == 'UnlockableTransport':
2951
raise errors.UnlockableTransport(find('bzrdir').root_transport)
2952
elif err.error_verb == 'LockFailed':
2953
raise errors.LockFailed(err.error_args[0], err.error_args[1])
2954
elif err.error_verb == 'TokenMismatch':
2955
raise errors.TokenMismatch(find('token'), '(remote token)')
2956
elif err.error_verb == 'Diverged':
2957
raise errors.DivergedBranches(find('branch'), find('other_branch'))
2958
elif err.error_verb == 'TipChangeRejected':
2959
raise errors.TipChangeRejected(err.error_args[0].decode('utf8'))
2960
elif err.error_verb == 'UnstackableBranchFormat':
2961
raise errors.UnstackableBranchFormat(*err.error_args)
2962
elif err.error_verb == 'UnstackableRepositoryFormat':
2963
raise errors.UnstackableRepositoryFormat(*err.error_args)
2964
elif err.error_verb == 'NotStacked':
2965
raise errors.NotStacked(branch=find('branch'))
2966
elif err.error_verb == 'PermissionDenied':
2968
if len(err.error_args) >= 2:
2969
extra = err.error_args[1]
2972
raise errors.PermissionDenied(path, extra=extra)
2973
elif err.error_verb == 'ReadError':
2975
raise errors.ReadError(path)
2976
elif err.error_verb == 'NoSuchFile':
2978
raise errors.NoSuchFile(path)
2979
elif err.error_verb == 'FileExists':
2980
raise errors.FileExists(err.error_args[0])
2981
elif err.error_verb == 'DirectoryNotEmpty':
2982
raise errors.DirectoryNotEmpty(err.error_args[0])
2983
elif err.error_verb == 'ShortReadvError':
2984
args = err.error_args
2985
raise errors.ShortReadvError(
2986
args[0], int(args[1]), int(args[2]), int(args[3]))
2987
elif err.error_verb in ('UnicodeEncodeError', 'UnicodeDecodeError'):
2988
encoding = str(err.error_args[0]) # encoding must always be a string
2989
val = err.error_args[1]
2990
start = int(err.error_args[2])
2991
end = int(err.error_args[3])
2992
reason = str(err.error_args[4]) # reason must always be a string
2993
if val.startswith('u:'):
2994
val = val[2:].decode('utf-8')
2995
elif val.startswith('s:'):
2996
val = val[2:].decode('base64')
2997
if err.error_verb == 'UnicodeDecodeError':
2998
raise UnicodeDecodeError(encoding, val, start, end, reason)
2999
elif err.error_verb == 'UnicodeEncodeError':
3000
raise UnicodeEncodeError(encoding, val, start, end, reason)
3001
elif err.error_verb == 'ReadOnlyError':
3002
raise errors.TransportNotPossible('readonly transport')
3003
raise errors.UnknownErrorFromSmartServer(err)