96
74
return self._client.call_with_body_bytes_expecting_body(
97
75
method, args, body_bytes)
98
except errors.ErrorFromSmartServer as err:
76
except errors.ErrorFromSmartServer, err:
99
77
self._translate_error(err, **err_context)
102
80
def response_tuple_to_repo_format(response):
103
81
"""Convert a response tuple describing a repository format to a format."""
104
82
format = RemoteRepositoryFormat()
105
format._rich_root_data = (response[0] == b'yes')
106
format._supports_tree_reference = (response[1] == b'yes')
107
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')
108
86
format._network_name = response[3]
112
# Note that RemoteBzrDirProber lives in breezy.bzrdir so breezy.bzr.remote
113
# does not have to be imported unless a remote format is involved.
115
class RemoteBzrDirFormat(_mod_bzrdir.BzrDirMetaFormat1):
116
"""Format representing bzrdirs accessed via a smart server"""
118
supports_workingtrees = False
120
colocated_branches = False
123
_mod_bzrdir.BzrDirMetaFormat1.__init__(self)
124
# XXX: It's a bit ugly that the network name is here, because we'd
125
# like to believe that format objects are stateless or at least
126
# immutable, However, we do at least avoid mutating the name after
127
# it's returned. See <https://bugs.launchpad.net/bzr/+bug/504102>
128
self._network_name = None
131
return "%s(_network_name=%r)" % (self.__class__.__name__,
134
def get_format_description(self):
135
if self._network_name:
137
real_format = controldir.network_format_registry.get(
142
return 'Remote: ' + real_format.get_format_description()
143
return 'bzr remote bzrdir'
145
def get_format_string(self):
146
raise NotImplementedError(self.get_format_string)
148
def network_name(self):
149
if self._network_name:
150
return self._network_name
152
raise AssertionError("No network name set.")
154
def initialize_on_transport(self, transport):
156
# hand off the request to the smart server
157
client_medium = transport.get_smart_medium()
158
except errors.NoSmartMedium:
159
# TODO: lookup the local format from a server hint.
160
local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
161
return local_dir_format.initialize_on_transport(transport)
162
client = _SmartClient(client_medium)
163
path = client.remote_path_from_transport(transport)
165
response = client.call(b'BzrDirFormat.initialize', path)
166
except errors.ErrorFromSmartServer as err:
167
_translate_error(err, path=path)
168
if response[0] != b'ok':
169
raise errors.SmartProtocolError(
170
'unexpected response code %s' % (response,))
171
format = RemoteBzrDirFormat()
172
self._supply_sub_formats_to(format)
173
return RemoteBzrDir(transport, format)
175
def parse_NoneTrueFalse(self, arg):
182
raise AssertionError("invalid arg %r" % arg)
184
def _serialize_NoneTrueFalse(self, arg):
191
def _serialize_NoneString(self, arg):
194
def initialize_on_transport_ex(self, transport, use_existing_dir=False,
195
create_prefix=False, force_new_repo=False, stacked_on=None,
196
stack_on_pwd=None, repo_format_name=None, make_working_trees=None,
199
# hand off the request to the smart server
200
client_medium = transport.get_smart_medium()
201
except errors.NoSmartMedium:
204
# Decline to open it if the server doesn't support our required
205
# version (3) so that the VFS-based transport will do it.
206
if client_medium.should_probe():
208
server_version = client_medium.protocol_version()
209
if server_version != '2':
213
except errors.SmartProtocolError:
214
# Apparently there's no usable smart server there, even though
215
# the medium supports the smart protocol.
220
client = _SmartClient(client_medium)
221
path = client.remote_path_from_transport(transport)
222
if client_medium._is_remote_before((1, 16)):
225
# TODO: lookup the local format from a server hint.
226
local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
227
self._supply_sub_formats_to(local_dir_format)
228
return local_dir_format.initialize_on_transport_ex(transport,
229
use_existing_dir=use_existing_dir, create_prefix=create_prefix,
230
force_new_repo=force_new_repo, stacked_on=stacked_on,
231
stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
232
make_working_trees=make_working_trees, shared_repo=shared_repo,
234
return self._initialize_on_transport_ex_rpc(client, path, transport,
235
use_existing_dir, create_prefix, force_new_repo, stacked_on,
236
stack_on_pwd, repo_format_name, make_working_trees, shared_repo)
238
def _initialize_on_transport_ex_rpc(self, client, path, transport,
239
use_existing_dir, create_prefix, force_new_repo, stacked_on,
240
stack_on_pwd, repo_format_name, make_working_trees, shared_repo):
242
args.append(self._serialize_NoneTrueFalse(use_existing_dir))
243
args.append(self._serialize_NoneTrueFalse(create_prefix))
244
args.append(self._serialize_NoneTrueFalse(force_new_repo))
245
args.append(self._serialize_NoneString(stacked_on))
246
# stack_on_pwd is often/usually our transport
249
stack_on_pwd = transport.relpath(stack_on_pwd).encode('utf-8')
252
except errors.PathNotChild:
254
args.append(self._serialize_NoneString(stack_on_pwd))
255
args.append(self._serialize_NoneString(repo_format_name))
256
args.append(self._serialize_NoneTrueFalse(make_working_trees))
257
args.append(self._serialize_NoneTrueFalse(shared_repo))
258
request_network_name = self._network_name or \
259
_mod_bzrdir.BzrDirFormat.get_default_format().network_name()
261
response = client.call(b'BzrDirFormat.initialize_ex_1.16',
262
request_network_name, path, *args)
263
except errors.UnknownSmartMethod:
264
client._medium._remember_remote_is_before((1, 16))
265
local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
266
self._supply_sub_formats_to(local_dir_format)
267
return local_dir_format.initialize_on_transport_ex(transport,
268
use_existing_dir=use_existing_dir, create_prefix=create_prefix,
269
force_new_repo=force_new_repo, stacked_on=stacked_on,
270
stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
271
make_working_trees=make_working_trees, shared_repo=shared_repo,
273
except errors.ErrorFromSmartServer as err:
274
_translate_error(err, path=path.decode('utf-8'))
275
repo_path = response[0]
276
bzrdir_name = response[6]
277
require_stacking = response[7]
278
require_stacking = self.parse_NoneTrueFalse(require_stacking)
279
format = RemoteBzrDirFormat()
280
format._network_name = bzrdir_name
281
self._supply_sub_formats_to(format)
282
bzrdir = RemoteBzrDir(transport, format, _client=client)
284
repo_format = response_tuple_to_repo_format(response[1:])
285
if repo_path == b'.':
287
repo_path = repo_path.decode('utf-8')
289
repo_bzrdir_format = RemoteBzrDirFormat()
290
repo_bzrdir_format._network_name = response[5]
291
repo_bzr = RemoteBzrDir(transport.clone(repo_path),
295
final_stack = response[8] or None
297
final_stack = final_stack.decode('utf-8')
298
final_stack_pwd = response[9] or None
300
final_stack_pwd = urlutils.join(
301
transport.base, final_stack_pwd.decode('utf-8'))
302
remote_repo = RemoteRepository(repo_bzr, repo_format)
303
if len(response) > 10:
304
# Updated server verb that locks remotely.
305
repo_lock_token = response[10] or None
306
remote_repo.lock_write(repo_lock_token, _skip_rpc=True)
308
remote_repo.dont_leave_lock_in_place()
310
remote_repo.lock_write()
311
policy = _mod_bzrdir.UseExistingRepository(remote_repo,
312
final_stack, final_stack_pwd, require_stacking)
313
policy.acquire_repository()
317
bzrdir._format.set_branch_format(self.get_branch_format())
319
# The repo has already been created, but we need to make sure that
320
# we'll make a stackable branch.
321
bzrdir._format.require_stacking(_skip_repo=True)
322
return remote_repo, bzrdir, require_stacking, policy
324
def _open(self, transport):
325
return RemoteBzrDir(transport, self)
327
def __eq__(self, other):
328
if not isinstance(other, RemoteBzrDirFormat):
330
return self.get_format_description() == other.get_format_description()
332
def __return_repository_format(self):
333
# Always return a RemoteRepositoryFormat object, but if a specific bzr
334
# repository format has been asked for, tell the RemoteRepositoryFormat
335
# that it should use that for init() etc.
336
result = RemoteRepositoryFormat()
337
custom_format = getattr(self, '_repository_format', None)
339
if isinstance(custom_format, RemoteRepositoryFormat):
342
# We will use the custom format to create repositories over the
343
# wire; expose its details like rich_root_data for code to
345
result._custom_format = custom_format
348
def get_branch_format(self):
349
result = _mod_bzrdir.BzrDirMetaFormat1.get_branch_format(self)
350
if not isinstance(result, RemoteBranchFormat):
351
new_result = RemoteBranchFormat()
352
new_result._custom_format = result
354
self.set_branch_format(new_result)
358
repository_format = property(__return_repository_format,
359
_mod_bzrdir.BzrDirMetaFormat1._set_repository_format) # .im_func)
362
class RemoteControlStore(_mod_config.IniFileStore):
363
"""Control store which attempts to use HPSS calls to retrieve control store.
365
Note that this is specific to bzr-based formats.
368
def __init__(self, bzrdir):
369
super(RemoteControlStore, self).__init__()
370
self.controldir = bzrdir
371
self._real_store = None
373
def lock_write(self, token=None):
375
return self._real_store.lock_write(token)
379
return self._real_store.unlock()
382
with self.lock_write():
383
# We need to be able to override the undecorated implementation
384
self.save_without_locking()
386
def save_without_locking(self):
387
super(RemoteControlStore, self).save()
389
def _ensure_real(self):
390
self.controldir._ensure_real()
391
if self._real_store is None:
392
self._real_store = _mod_config.ControlStore(self.controldir)
394
def external_url(self):
395
return urlutils.join(self.branch.user_url, 'control.conf')
397
def _load_content(self):
398
medium = self.controldir._client._medium
399
path = self.controldir._path_for_remote_call(self.controldir._client)
401
response, handler = self.controldir._call_expecting_body(
402
b'BzrDir.get_config_file', path)
403
except errors.UnknownSmartMethod:
405
return self._real_store._load_content()
406
if len(response) and response[0] != b'ok':
407
raise errors.UnexpectedSmartServerResponse(response)
408
return handler.read_body_bytes()
410
def _save_content(self, content):
411
# FIXME JRV 2011-11-22: Ideally this should use a
412
# HPSS call too, but at the moment it is not possible
413
# to write lock control directories.
415
return self._real_store._save_content(content)
418
class RemoteBzrDir(_mod_bzrdir.BzrDir, _RpcHelper):
90
# Note: RemoteBzrDirFormat is in bzrdir.py
92
class RemoteBzrDir(BzrDir, _RpcHelper):
419
93
"""Control directory on a remote server, accessed via bzr:// or similar."""
421
95
def __init__(self, transport, format, _client=None, _force_probe=False):
555
187
medium = self._client._medium
556
188
if medium._is_remote_before((1, 13)):
557
189
return self._vfs_cloning_metadir(require_stacking=require_stacking)
558
verb = b'BzrDir.cloning_metadir'
190
verb = 'BzrDir.cloning_metadir'
559
191
if require_stacking:
563
195
path = self._path_for_remote_call(self._client)
565
197
response = self._call(verb, path, stacking)
566
198
except errors.UnknownSmartMethod:
567
199
medium._remember_remote_is_before((1, 13))
568
200
return self._vfs_cloning_metadir(require_stacking=require_stacking)
569
except errors.UnknownErrorFromSmartServer as err:
570
if err.error_tuple != (b'BranchReference',):
201
except errors.UnknownErrorFromSmartServer, err:
202
if err.error_tuple != ('BranchReference',):
572
204
# We need to resolve the branch reference to determine the
573
205
# cloning_metadir. This causes unnecessary RPCs to open the
574
206
# referenced branch (and bzrdir, etc) but only when the caller
575
207
# didn't already resolve the branch reference.
576
208
referenced_branch = self.open_branch()
577
return referenced_branch.controldir.cloning_metadir()
209
return referenced_branch.bzrdir.cloning_metadir()
578
210
if len(response) != 3:
579
211
raise errors.UnexpectedSmartServerResponse(response)
580
212
control_name, repo_name, branch_info = response
581
213
if len(branch_info) != 2:
582
214
raise errors.UnexpectedSmartServerResponse(response)
583
215
branch_ref, branch_name = branch_info
585
format = controldir.network_format_registry.get(control_name)
587
raise errors.UnknownFormatError(
588
kind='control', format=control_name)
216
format = bzrdir.network_format_registry.get(control_name)
592
format.repository_format = _mod_repository.network_format_registry.get(
595
raise errors.UnknownFormatError(kind='repository',
597
if branch_ref == b'ref':
218
format.repository_format = repository.network_format_registry.get(
220
if branch_ref == 'ref':
598
221
# XXX: we need possible_transports here to avoid reopening the
599
222
# connection to the referenced location
600
ref_bzrdir = _mod_bzrdir.BzrDir.open(branch_name)
223
ref_bzrdir = BzrDir.open(branch_name)
601
224
branch_format = ref_bzrdir.cloning_metadir().get_branch_format()
602
225
format.set_branch_format(branch_format)
603
elif branch_ref == b'branch':
226
elif branch_ref == 'branch':
606
branch_format = branch.network_format_registry.get(
609
raise errors.UnknownFormatError(kind='branch',
611
format.set_branch_format(branch_format)
228
format.set_branch_format(
229
branch.network_format_registry.get(branch_name))
613
231
raise errors.UnexpectedSmartServerResponse(response)
698
281
b = self.open_branch(name=name)
701
def branch_names(self):
702
path = self._path_for_remote_call(self._client)
704
response, handler = self._call_expecting_body(
705
b'BzrDir.get_branches', path)
706
except errors.UnknownSmartMethod:
708
return self._real_bzrdir.branch_names()
709
if response[0] != b"success":
710
raise errors.UnexpectedSmartServerResponse(response)
711
body = bencode.bdecode(handler.read_body_bytes())
713
for name, value in body.items():
714
name = name.decode('utf-8')
718
def get_branches(self, possible_transports=None, ignore_fallbacks=False):
719
path = self._path_for_remote_call(self._client)
721
response, handler = self._call_expecting_body(
722
b'BzrDir.get_branches', path)
723
except errors.UnknownSmartMethod:
725
return self._real_bzrdir.get_branches()
726
if response[0] != b"success":
727
raise errors.UnexpectedSmartServerResponse(response)
728
body = bencode.bdecode(handler.read_body_bytes())
730
for name, value in body.items():
731
name = name.decode('utf-8')
732
ret[name] = self._open_branch(
733
name, value[0].decode('ascii'), value[1],
734
possible_transports=possible_transports,
735
ignore_fallbacks=ignore_fallbacks)
738
def set_branch_reference(self, target_branch, name=None):
739
"""See BzrDir.set_branch_reference()."""
741
name = self._get_selected_branch()
743
raise errors.NoColocatedBranchSupport(self)
745
return self._real_bzrdir.set_branch_reference(target_branch, name=name)
747
284
def get_branch_reference(self, name=None):
748
285
"""See BzrDir.get_branch_reference()."""
750
name = self._get_selected_branch()
287
# XXX JRV20100304: Support opening colocated branches
752
288
raise errors.NoColocatedBranchSupport(self)
753
289
response = self._get_branch_reference()
754
290
if response[0] == 'ref':
755
return response[1].decode('utf-8')
759
295
def _get_branch_reference(self):
760
"""Get branch reference information
762
:return: Tuple with (kind, location_or_format)
763
if kind == 'ref', then location_or_format contains a location
764
otherwise, it contains a format name
766
296
path = self._path_for_remote_call(self._client)
767
297
medium = self._client._medium
768
298
candidate_calls = [
769
(b'BzrDir.open_branchV3', (2, 1)),
770
(b'BzrDir.open_branchV2', (1, 13)),
771
(b'BzrDir.open_branch', None),
299
('BzrDir.open_branchV3', (2, 1)),
300
('BzrDir.open_branchV2', (1, 13)),
301
('BzrDir.open_branch', None),
773
303
for verb, required_version in candidate_calls:
774
304
if required_version and medium._is_remote_before(required_version):
781
311
medium._remember_remote_is_before(required_version)
784
if verb == b'BzrDir.open_branch':
785
if response[0] != b'ok':
314
if verb == 'BzrDir.open_branch':
315
if response[0] != 'ok':
786
316
raise errors.UnexpectedSmartServerResponse(response)
787
if response[1] != b'':
317
if response[1] != '':
788
318
return ('ref', response[1])
790
return ('branch', b'')
791
if response[0] not in (b'ref', b'branch'):
320
return ('branch', '')
321
if response[0] not in ('ref', 'branch'):
792
322
raise errors.UnexpectedSmartServerResponse(response)
793
return (response[0].decode('ascii'), response[1])
795
325
def _get_tree_branch(self, name=None):
796
326
"""See BzrDir._get_tree_branch()."""
797
327
return None, self.open_branch(name=name)
799
def _open_branch(self, name, kind, location_or_format,
800
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':
802
340
# a branch reference, use the existing BranchReference logic.
803
341
format = BranchReferenceFormat()
804
ref_loc = urlutils.join(self.user_url, location_or_format.decode('utf-8'))
805
342
return format.open(self, name=name, _found=True,
807
ignore_fallbacks=ignore_fallbacks,
808
possible_transports=possible_transports)
809
branch_format_name = location_or_format
343
location=response[1], ignore_fallbacks=ignore_fallbacks)
344
branch_format_name = response[1]
810
345
if not branch_format_name:
811
346
branch_format_name = None
812
347
format = RemoteBranchFormat(network_name=branch_format_name)
813
348
return RemoteBranch(self, self.find_repository(), format=format,
814
setup_stacking=not ignore_fallbacks, name=name,
815
possible_transports=possible_transports)
817
def open_branch(self, name=None, unsupported=False,
818
ignore_fallbacks=False, possible_transports=None):
820
name = self._get_selected_branch()
822
raise errors.NoColocatedBranchSupport(self)
824
raise NotImplementedError(
825
'unsupported flag support not implemented yet.')
826
if self._next_open_branch_result is not None:
827
# See create_branch for details.
828
result = self._next_open_branch_result
829
self._next_open_branch_result = None
831
response = self._get_branch_reference()
832
return self._open_branch(name, response[0], response[1],
833
possible_transports=possible_transports,
834
ignore_fallbacks=ignore_fallbacks)
349
setup_stacking=not ignore_fallbacks, name=name)
836
351
def _open_repo_v1(self, path):
837
verb = b'BzrDir.find_repository'
352
verb = 'BzrDir.find_repository'
838
353
response = self._call(verb, path)
839
if response[0] != b'ok':
354
if response[0] != 'ok':
840
355
raise errors.UnexpectedSmartServerResponse(response)
841
356
# servers that only support the v1 method don't support external
842
357
# references either.
843
358
self._ensure_real()
844
359
repo = self._real_bzrdir.open_repository()
845
response = response + (b'no', repo._format.network_name())
360
response = response + ('no', repo._format.network_name())
846
361
return response, repo
848
363
def _open_repo_v2(self, path):
849
verb = b'BzrDir.find_repositoryV2'
364
verb = 'BzrDir.find_repositoryV2'
850
365
response = self._call(verb, path)
851
if response[0] != b'ok':
366
if response[0] != 'ok':
852
367
raise errors.UnexpectedSmartServerResponse(response)
853
368
self._ensure_real()
854
369
repo = self._real_bzrdir.open_repository()
945
445
"""Upgrading of remote bzrdirs is not supported yet."""
948
def needs_format_conversion(self, format):
448
def needs_format_conversion(self, format=None):
949
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)
952
461
def _get_config(self):
953
462
return RemoteBzrDirConfig(self)
955
def _get_config_store(self):
956
return RemoteControlStore(self)
959
class RemoteInventoryTree(InventoryRevisionTree):
961
def __init__(self, repository, inv, revision_id):
962
super(RemoteInventoryTree, self).__init__(repository, inv, revision_id)
964
def archive(self, format, name, root=None, subdir=None, force_mtime=None):
965
ret = self._repository._revision_archive(
966
self.get_revision_id(), format, name, root, subdir,
967
force_mtime=force_mtime)
969
return super(RemoteInventoryTree, self).archive(
970
format, name, root, subdir, force_mtime=force_mtime)
973
def annotate_iter(self, path,
974
default_revision=_mod_revision.CURRENT_REVISION):
975
"""Return an iterator of revision_id, line tuples.
977
For working trees (and mutable trees in general), the special
978
revision_id 'current:' will be used for lines that are new in this
979
tree, e.g. uncommitted changes.
980
:param default_revision: For lines that don't match a basis, mark them
981
with this revision id. Not all implementations will make use of
984
ret = self._repository._annotate_file_revision(
985
self.get_revision_id(), path, file_id=None,
986
default_revision=default_revision)
988
return super(RemoteInventoryTree, self).annotate_iter(
989
path, default_revision=default_revision)
993
class RemoteRepositoryFormat(vf_repository.VersionedFileRepositoryFormat):
465
class RemoteRepositoryFormat(repository.RepositoryFormat):
994
466
"""Format for repositories accessed over a _SmartClient.
996
468
Instances of this repository are represented by RemoteRepository
1083
533
self._custom_format.supports_tree_reference
1084
534
return self._supports_tree_reference
1087
def revision_graph_can_have_wrong_parents(self):
1088
if self._revision_graph_can_have_wrong_parents is None:
1090
self._revision_graph_can_have_wrong_parents = \
1091
self._custom_format.revision_graph_can_have_wrong_parents
1092
return self._revision_graph_can_have_wrong_parents
1094
def _vfs_initialize(self, a_controldir, shared):
536
def _vfs_initialize(self, a_bzrdir, shared):
1095
537
"""Helper for common code in initialize."""
1096
538
if self._custom_format:
1097
539
# Custom format requested
1098
result = self._custom_format.initialize(
1099
a_controldir, shared=shared)
540
result = self._custom_format.initialize(a_bzrdir, shared=shared)
1100
541
elif self._creating_bzrdir is not None:
1101
542
# Use the format that the repository we were created to back
1103
544
prior_repo = self._creating_bzrdir.open_repository()
1104
545
prior_repo._ensure_real()
1105
546
result = prior_repo._real_repository._format.initialize(
1106
a_controldir, shared=shared)
547
a_bzrdir, shared=shared)
1108
549
# assume that a_bzr is a RemoteBzrDir but the smart server didn't
1109
550
# support remote initialization.
1110
551
# We delegate to a real object at this point (as RemoteBzrDir
1111
552
# delegate to the repository format which would lead to infinite
1112
# recursion if we just called a_controldir.create_repository.
1113
a_controldir._ensure_real()
1114
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)
1115
556
if not isinstance(result, RemoteRepository):
1116
return self.open(a_controldir)
557
return self.open(a_bzrdir)
1120
def initialize(self, a_controldir, shared=False):
561
def initialize(self, a_bzrdir, shared=False):
1121
562
# Being asked to create on a non RemoteBzrDir:
1122
if not isinstance(a_controldir, RemoteBzrDir):
1123
return self._vfs_initialize(a_controldir, shared)
1124
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
1125
566
if medium._is_remote_before((1, 13)):
1126
return self._vfs_initialize(a_controldir, shared)
567
return self._vfs_initialize(a_bzrdir, shared)
1127
568
# Creating on a remote bzr dir.
1128
569
# 1) get the network name to use.
1129
570
if self._custom_format:
1131
572
elif self._network_name:
1132
573
network_name = self._network_name
1134
# Select the current breezy default and ask for that.
1135
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')()
1137
577
reference_format = reference_bzrdir_format.repository_format
1138
578
network_name = reference_format.network_name()
1139
579
# 2) try direct creation via RPC
1140
path = a_controldir._path_for_remote_call(a_controldir._client)
1141
verb = b'BzrDir.create_repository'
580
path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
581
verb = 'BzrDir.create_repository'
1143
shared_str = b'True'
1145
shared_str = b'False'
1147
response = a_controldir._call(verb, path, network_name, shared_str)
587
response = a_bzrdir._call(verb, path, network_name, shared_str)
1148
588
except errors.UnknownSmartMethod:
1149
589
# Fallback - use vfs methods
1150
590
medium._remember_remote_is_before((1, 13))
1151
return self._vfs_initialize(a_controldir, shared)
591
return self._vfs_initialize(a_bzrdir, shared)
1153
593
# Turn the response into a RemoteRepository object.
1154
594
format = response_tuple_to_repo_format(response[1:])
1155
595
# Used to support creating a real format instance when needed.
1156
format._creating_bzrdir = a_controldir
1157
remote_repo = RemoteRepository(a_controldir, format)
596
format._creating_bzrdir = a_bzrdir
597
remote_repo = RemoteRepository(a_bzrdir, format)
1158
598
format._creating_repo = remote_repo
1159
599
return remote_repo
1161
def open(self, a_controldir):
1162
if not isinstance(a_controldir, RemoteBzrDir):
1163
raise AssertionError('%r is not a RemoteBzrDir' % (a_controldir,))
1164
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()
1166
606
def _ensure_real(self):
1167
607
if self._custom_format is None:
1169
self._custom_format = _mod_repository.network_format_registry.get(
1172
raise errors.UnknownFormatError(kind='repository',
1173
format=self._network_name)
608
self._custom_format = repository.network_format_registry.get(
1176
612
def _fetch_order(self):
1540
913
# TODO: Move to RepositoryBase and unify with the regular Repository
1541
914
# one; unfortunately the tests rely on slightly different behaviour at
1542
915
# present -- mbp 20090710
1543
return (self.__class__ is other.__class__
1544
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)
1546
919
def get_graph(self, other_repository=None):
1547
920
"""Return the graph for this repository format"""
1548
921
parents_provider = self._make_parents_provider(other_repository)
1549
922
return graph.Graph(parents_provider)
1551
925
def get_known_graph_ancestry(self, revision_ids):
1552
926
"""Return the known graph for a set of revision ids and their ancestors.
1554
with self.lock_read():
1555
revision_graph = dict(((key, value) for key, value in
1556
self.get_graph().iter_ancestry(revision_ids) if value is not None))
1557
revision_graph = _mod_repository._strip_NULL_ghosts(revision_graph)
1558
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)
1560
933
def gather_stats(self, revid=None, committers=None):
1561
934
"""See Repository.gather_stats()."""
1562
path = self.controldir._path_for_remote_call(self._client)
935
path = self.bzrdir._path_for_remote_call(self._client)
1563
936
# revid can be None to indicate no revisions, not just NULL_REVISION
1564
if revid is None or _mod_revision.is_null(revid):
937
if revid is None or revision.is_null(revid):
1567
940
fmt_revid = revid
1568
941
if committers is None or not committers:
1569
fmt_committers = b'no'
942
fmt_committers = 'no'
1571
fmt_committers = b'yes'
944
fmt_committers = 'yes'
1572
945
response_tuple, response_handler = self._call_expecting_body(
1573
b'Repository.gather_stats', path, fmt_revid, fmt_committers)
1574
if response_tuple[0] != b'ok':
946
'Repository.gather_stats', path, fmt_revid, fmt_committers)
947
if response_tuple[0] != 'ok':
1575
948
raise errors.UnexpectedSmartServerResponse(response_tuple)
1577
950
body = response_handler.read_body_bytes()
1579
for line in body.split(b'\n'):
952
for line in body.split('\n'):
1582
key, val_text = line.split(b':')
1583
key = key.decode('ascii')
955
key, val_text = line.split(':')
1584
956
if key in ('revisions', 'size', 'committers'):
1585
957
result[key] = int(val_text)
1586
958
elif key in ('firstrev', 'latestrev'):
1587
values = val_text.split(b' ')[1:]
1588
result[key] = (float(values[0]), int(values[1]))
959
values = val_text.split(' ')[1:]
960
result[key] = (float(values[0]), long(values[1]))
1869
1204
raise errors.UnexpectedSmartServerResponse(response)
1871
1206
def sprout(self, to_bzrdir, revision_id=None):
1872
"""Create a descendent repository for new development.
1874
Unlike clone, this does not copy the settings of the repository.
1876
with self.lock_read():
1877
dest_repo = self._create_sprouting_repo(to_bzrdir, shared=False)
1878
dest_repo.fetch(self, revision_id=revision_id)
1881
def _create_sprouting_repo(self, a_controldir, shared):
1882
if not isinstance(a_controldir._format, self.controldir._format.__class__):
1883
# use target default format.
1884
dest_repo = a_controldir.create_repository()
1886
# Most control formats need the repository to be specifically
1887
# created, but on some old all-in-one formats it's not needed
1889
dest_repo = self._format.initialize(
1890
a_controldir, shared=shared)
1891
except errors.UninitializableFormat:
1892
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)
1893
1212
return dest_repo
1895
# 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.
1897
1216
def revision_tree(self, revision_id):
1898
with self.lock_read():
1899
revision_id = _mod_revision.ensure_null(revision_id)
1900
if revision_id == _mod_revision.NULL_REVISION:
1901
return InventoryRevisionTree(self,
1902
Inventory(root_id=None), _mod_revision.NULL_REVISION)
1904
return list(self.revision_trees([revision_id]))[0]
1218
return self._real_repository.revision_tree(revision_id)
1906
1220
def get_serializer_format(self):
1907
path = self.controldir._path_for_remote_call(self._client)
1909
response = self._call(b'VersionedFileRepository.get_serializer_format',
1911
except errors.UnknownSmartMethod:
1913
return self._real_repository.get_serializer_format()
1914
if response[0] != b'ok':
1915
raise errors.UnexpectedSmartServerResponse(response)
1222
return self._real_repository.get_serializer_format()
1918
1224
def get_commit_builder(self, branch, parents, config, timestamp=None,
1919
1225
timezone=None, committer=None, revprops=None,
1920
revision_id=None, lossy=False):
1921
"""Obtain a CommitBuilder for this repository.
1923
:param branch: Branch to commit to.
1924
:param parents: Revision ids of the parents of the new revision.
1925
:param config: Configuration to use.
1926
:param timestamp: Optional timestamp recorded for commit.
1927
:param timezone: Optional timezone for timestamp.
1928
:param committer: Optional committer to set for commit.
1929
:param revprops: Optional dictionary of revision properties.
1930
:param revision_id: Optional revision id.
1931
:param lossy: Whether to discard data that can not be natively
1932
represented, when pushing to a foreign VCS
1934
if self._fallback_repositories and not self._format.supports_chks:
1935
raise errors.BzrError("Cannot commit directly to a stacked branch"
1936
" in pre-2a formats. See "
1937
"https://bugs.launchpad.net/bzr/+bug/375013 for details.")
1938
commit_builder_kls = vf_repository.VersionedFileCommitBuilder
1939
result = commit_builder_kls(self, parents, config,
1940
timestamp, timezone, committer, revprops, revision_id,
1942
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)
1945
1236
def add_fallback_repository(self, repository):
1946
1237
"""Add a repository to use for looking up data not held locally.
1985
1275
return self._real_repository.add_inventory(revid, inv, parents)
1987
1277
def add_inventory_by_delta(self, basis_revision_id, delta, new_revision_id,
1988
parents, basis_inv=None, propagate_caches=False):
1278
parents, basis_inv=None, propagate_caches=False):
1989
1279
self._ensure_real()
1990
1280
return self._real_repository.add_inventory_by_delta(basis_revision_id,
1991
delta, new_revision_id, parents, basis_inv=basis_inv,
1992
propagate_caches=propagate_caches)
1994
def add_revision(self, revision_id, rev, inv=None):
1995
_mod_revision.check_not_reserved_id(revision_id)
1996
key = (revision_id,)
1997
# check inventory present
1998
if not self.inventories.get_parent_map([key]):
2000
raise errors.WeaveRevisionNotPresent(revision_id,
2003
# yes, this is not suitable for adding with ghosts.
2004
rev.inventory_sha1 = self.add_inventory(revision_id, inv,
2007
rev.inventory_sha1 = self.inventories.get_sha1s([key])[key]
2008
self._add_revision(rev)
2010
def _add_revision(self, rev):
2011
if self._real_repository is not None:
2012
return self._real_repository._add_revision(rev)
2013
lines = self._serializer.write_revision_to_lines(rev)
2014
key = (rev.revision_id,)
2015
parents = tuple((parent,) for parent in rev.parent_ids)
2016
self._write_group_tokens, missing_keys = self._get_sink().insert_stream(
2017
[('revisions', [ChunkedContentFactory(key, parents, None, lines, chunks_are_lines=True)])],
2018
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)
2020
1290
def get_inventory(self, revision_id):
2021
with self.lock_read():
2022
return list(self.iter_inventories([revision_id]))[0]
2024
def _iter_inventories_rpc(self, revision_ids, ordering):
2025
if ordering is None:
2026
ordering = 'unordered'
2027
path = self.controldir._path_for_remote_call(self._client)
2028
body = b"\n".join(revision_ids)
2029
response_tuple, response_handler = (
2030
self._call_with_body_bytes_expecting_body(
2031
b"VersionedFileRepository.get_inventories",
2032
(path, ordering.encode('ascii')), body))
2033
if response_tuple[0] != b"ok":
2034
raise errors.UnexpectedSmartServerResponse(response_tuple)
2035
deserializer = inventory_delta.InventoryDeltaDeserializer()
2036
byte_stream = response_handler.read_streamed_body()
2037
decoded = smart_repo._byte_stream_to_stream(byte_stream)
2039
# no results whatsoever
2041
src_format, stream = decoded
2042
if src_format.network_name() != self._format.network_name():
2043
raise AssertionError(
2044
"Mismatched RemoteRepository and stream src %r, %r" % (
2045
src_format.network_name(), self._format.network_name()))
2046
# ignore the src format, it's not really relevant
2047
prev_inv = Inventory(root_id=None,
2048
revision_id=_mod_revision.NULL_REVISION)
2049
# there should be just one substream, with inventory deltas
2051
substream_kind, substream = next(stream)
2052
except StopIteration:
2054
if substream_kind != "inventory-deltas":
2055
raise AssertionError(
2056
"Unexpected stream %r received" % substream_kind)
2057
for record in substream:
2058
(parent_id, new_id, versioned_root, tree_references, invdelta) = (
2059
deserializer.parse_text_bytes(record.get_bytes_as("lines")))
2060
if parent_id != prev_inv.revision_id:
2061
raise AssertionError("invalid base %r != %r" % (parent_id,
2062
prev_inv.revision_id))
2063
inv = prev_inv.create_by_apply_delta(invdelta, new_id)
2064
yield inv, inv.revision_id
2067
def _iter_inventories_vfs(self, revision_ids, ordering=None):
2068
1291
self._ensure_real()
2069
return self._real_repository._iter_inventories(revision_ids, ordering)
1292
return self._real_repository.get_inventory(revision_id)
2071
1294
def iter_inventories(self, revision_ids, ordering=None):
2072
"""Get many inventories by revision_ids.
2074
This will buffer some or all of the texts used in constructing the
2075
inventories in memory, but will only parse a single inventory at a
2078
:param revision_ids: The expected revision ids of the inventories.
2079
:param ordering: optional ordering, e.g. 'topological'. If not
2080
specified, the order of revision_ids will be preserved (by
2081
buffering if necessary).
2082
:return: An iterator of inventories.
2084
if ((None in revision_ids) or
2085
(_mod_revision.NULL_REVISION in revision_ids)):
2086
raise ValueError('cannot get null revision inventory')
2087
for inv, revid in self._iter_inventories(revision_ids, ordering):
2089
raise errors.NoSuchRevision(self, revid)
2092
def _iter_inventories(self, revision_ids, ordering=None):
2093
if len(revision_ids) == 0:
2095
missing = set(revision_ids)
2096
if ordering is None:
2097
order_as_requested = True
2099
order = list(revision_ids)
2101
next_revid = order.pop()
2103
order_as_requested = False
2104
if ordering != 'unordered' and self._fallback_repositories:
2105
raise ValueError('unsupported ordering %r' % ordering)
2106
iter_inv_fns = [self._iter_inventories_rpc] + [
2107
fallback._iter_inventories for fallback in
2108
self._fallback_repositories]
2110
for iter_inv in iter_inv_fns:
2111
request = [revid for revid in revision_ids if revid in missing]
2112
for inv, revid in iter_inv(request, ordering):
2115
missing.remove(inv.revision_id)
2116
if ordering != 'unordered':
2120
if order_as_requested:
2121
# Yield as many results as we can while preserving order.
2122
while next_revid in invs:
2123
inv = invs.pop(next_revid)
2124
yield inv, inv.revision_id
2126
next_revid = order.pop()
2128
# We still want to fully consume the stream, just
2129
# in case it is not actually finished at this point
2132
except errors.UnknownSmartMethod:
2133
for inv, revid in self._iter_inventories_vfs(revision_ids, ordering):
2137
if order_as_requested:
2138
if next_revid is not None:
2139
yield None, next_revid
2142
yield invs.get(revid), revid
2145
yield None, missing.pop()
1296
return self._real_repository.iter_inventories(revision_ids, ordering)
2147
1299
def get_revision(self, revision_id):
2148
with self.lock_read():
2149
return self.get_revisions([revision_id])[0]
1301
return self._real_repository.get_revision(revision_id)
2151
1303
def get_transaction(self):
2152
1304
self._ensure_real()
2153
1305
return self._real_repository.get_transaction()
2155
def clone(self, a_controldir, revision_id=None):
2156
with self.lock_read():
2157
dest_repo = self._create_sprouting_repo(
2158
a_controldir, shared=self.is_shared())
2159
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)
2162
1312
def make_working_trees(self):
2163
1313
"""See Repository.make_working_trees"""
2164
path = self.controldir._path_for_remote_call(self._client)
2166
response = self._call(b'Repository.make_working_trees', path)
2167
except errors.UnknownSmartMethod:
2169
return self._real_repository.make_working_trees()
2170
if response[0] not in (b'yes', b'no'):
2171
raise SmartProtocolError(
2172
'unexpected response code %s' % (response,))
2173
return response[0] == b'yes'
1315
return self._real_repository.make_working_trees()
2175
1317
def refresh_data(self):
2176
1318
"""Re-read any data needed to synchronise with disk.
2178
1320
This method is intended to be called after another repository instance
2179
1321
(such as one used by a smart server) has inserted data into the
2180
1322
repository. On all repositories this will work outside of write groups.
2181
Some repository formats (pack and newer for breezy native formats)
1323
Some repository formats (pack and newer for bzrlib native formats)
2182
1324
support refresh_data inside write groups. If called inside a write
2183
1325
group on a repository that does not support refreshing in a write group
2184
1326
IsInWriteGroupError will be raised.
2186
1328
if self._real_repository is not None:
2187
1329
self._real_repository.refresh_data()
2188
# Refresh the parents cache for this object
2189
self._unstacked_provider.disable_cache()
2190
self._unstacked_provider.enable_cache()
2192
1331
def revision_ids_to_search_result(self, result_set):
2193
1332
"""Convert a set of revision ids to a graph SearchResult."""
2194
1333
result_parents = set()
2195
for parents in self.get_graph().get_parent_map(result_set).values():
1334
for parents in self.get_graph().get_parent_map(
1335
result_set).itervalues():
2196
1336
result_parents.update(parents)
2197
1337
included_keys = result_set.intersection(result_parents)
2198
1338
start_keys = result_set.difference(included_keys)
2199
1339
exclude_keys = result_parents.difference(result_set)
2200
result = vf_search.SearchResult(start_keys, exclude_keys,
2201
len(result_set), result_set)
1340
result = graph.SearchResult(start_keys, exclude_keys,
1341
len(result_set), result_set)
2204
def search_missing_revision_ids(self, other,
2205
find_ghosts=True, revision_ids=None, if_present_ids=None,
1345
def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
2207
1346
"""Return the revision ids that other has that this does not.
2209
1348
These are returned in topological order.
2211
1350
revision_id: only return revision ids included by revision_id.
2213
with self.lock_read():
2214
inter_repo = _mod_repository.InterRepository.get(other, self)
2215
return inter_repo.search_missing_revision_ids(
2216
find_ghosts=find_ghosts, revision_ids=revision_ids,
2217
if_present_ids=if_present_ids, limit=limit)
1352
return repository.InterRepository.get(
1353
other, self).search_missing_revision_ids(revision_id, find_ghosts)
2219
def fetch(self, source, revision_id=None, find_ghosts=False,
2220
fetch_spec=None, lossy=False):
1355
def fetch(self, source, revision_id=None, pb=None, find_ghosts=False,
2221
1357
# No base implementation to use as RemoteRepository is not a subclass
2222
1358
# of Repository; so this is a copy of Repository.fetch().
2223
1359
if fetch_spec is not None and revision_id is not None:
2261
1397
return self._real_repository._get_versioned_file_checker(
2262
1398
revisions, revision_versions_cache)
2264
def _iter_files_bytes_rpc(self, desired_files, absent):
2265
path = self.controldir._path_for_remote_call(self._client)
2268
for (file_id, revid, identifier) in desired_files:
2269
lines.append(b''.join([
2273
identifiers.append(identifier)
2274
(response_tuple, response_handler) = (
2275
self._call_with_body_bytes_expecting_body(
2276
b"Repository.iter_files_bytes", (path, ), b"\n".join(lines)))
2277
if response_tuple != (b'ok', ):
2278
response_handler.cancel_read_body()
2279
raise errors.UnexpectedSmartServerResponse(response_tuple)
2280
byte_stream = response_handler.read_streamed_body()
2282
def decompress_stream(start, byte_stream, unused):
2283
decompressor = zlib.decompressobj()
2284
yield decompressor.decompress(start)
2285
while decompressor.unused_data == b"":
2287
data = next(byte_stream)
2288
except StopIteration:
2290
yield decompressor.decompress(data)
2291
yield decompressor.flush()
2292
unused.append(decompressor.unused_data)
2295
while b"\n" not in unused:
2297
unused += next(byte_stream)
2298
except StopIteration:
2300
header, rest = unused.split(b"\n", 1)
2301
args = header.split(b"\0")
2302
if args[0] == b"absent":
2303
absent[identifiers[int(args[3])]] = (args[1], args[2])
2306
elif args[0] == b"ok":
2309
raise errors.UnexpectedSmartServerResponse(args)
2311
yield (identifiers[idx],
2312
decompress_stream(rest, byte_stream, unused_chunks))
2313
unused = b"".join(unused_chunks)
2315
1400
def iter_files_bytes(self, desired_files):
2316
1401
"""See Repository.iter_file_bytes.
2320
for (identifier, bytes_iterator) in self._iter_files_bytes_rpc(
2321
desired_files, absent):
2322
yield identifier, bytes_iterator
2323
for fallback in self._fallback_repositories:
2326
desired_files = [(key[0], key[1], identifier)
2327
for identifier, key in absent.items()]
2328
for (identifier, bytes_iterator) in fallback.iter_files_bytes(desired_files):
2329
del absent[identifier]
2330
yield identifier, bytes_iterator
2332
# There may be more missing items, but raise an exception
2334
missing_identifier = next(iter(absent))
2335
missing_key = absent[missing_identifier]
2336
raise errors.RevisionNotPresent(revision_id=missing_key[1],
2337
file_id=missing_key[0])
2338
except errors.UnknownSmartMethod:
2340
for (identifier, bytes_iterator) in (
2341
self._real_repository.iter_files_bytes(desired_files)):
2342
yield identifier, bytes_iterator
2344
def get_cached_parent_map(self, revision_ids):
2345
"""See breezy.CachingParentsProvider.get_cached_parent_map"""
2346
return self._unstacked_provider.get_cached_parent_map(revision_ids)
1404
return self._real_repository.iter_files_bytes(desired_files)
2348
1406
def get_parent_map(self, revision_ids):
2349
"""See breezy.Graph.get_parent_map()."""
1407
"""See bzrlib.Graph.get_parent_map()."""
2350
1408
return self._make_parents_provider().get_parent_map(revision_ids)
2352
1410
def _get_parent_map_rpc(self, keys):
2469
1538
revision_graph[d[0]] = (NULL_REVISION,)
2470
1539
return revision_graph
2472
1542
def get_signature_text(self, revision_id):
2473
with self.lock_read():
2474
path = self.controldir._path_for_remote_call(self._client)
2476
response_tuple, response_handler = self._call_expecting_body(
2477
b'Repository.get_revision_signature_text', path, revision_id)
2478
except errors.UnknownSmartMethod:
2480
return self._real_repository.get_signature_text(revision_id)
2481
except errors.NoSuchRevision as err:
2482
for fallback in self._fallback_repositories:
2484
return fallback.get_signature_text(revision_id)
2485
except errors.NoSuchRevision:
2489
if response_tuple[0] != b'ok':
2490
raise errors.UnexpectedSmartServerResponse(response_tuple)
2491
return response_handler.read_body_bytes()
1544
return self._real_repository.get_signature_text(revision_id)
2493
1547
def _get_inventory_xml(self, revision_id):
2494
with self.lock_read():
2495
# This call is used by older working tree formats,
2496
# which stored a serialized basis inventory.
2498
return self._real_repository._get_inventory_xml(revision_id)
1549
return self._real_repository._get_inventory_xml(revision_id)
2500
1551
def reconcile(self, other=None, thorough=False):
2501
from ..reconcile import ReconcileResult
2502
with self.lock_write():
2503
path = self.controldir._path_for_remote_call(self._client)
2505
response, handler = self._call_expecting_body(
2506
b'Repository.reconcile', path, self._lock_token)
2507
except (errors.UnknownSmartMethod, errors.TokenLockingNotSupported):
2509
return self._real_repository.reconcile(other=other, thorough=thorough)
2510
if response != (b'ok', ):
2511
raise errors.UnexpectedSmartServerResponse(response)
2512
body = handler.read_body_bytes()
2513
result = ReconcileResult()
2514
result.garbage_inventories = None
2515
result.inconsistent_parents = None
2516
result.aborted = None
2517
for line in body.split(b'\n'):
2520
key, val_text = line.split(b':')
2521
if key == b"garbage_inventories":
2522
result.garbage_inventories = int(val_text)
2523
elif key == b"inconsistent_parents":
2524
result.inconsistent_parents = int(val_text)
2526
mutter("unknown reconcile key %r" % key)
1553
return self._real_repository.reconcile(other=other, thorough=thorough)
2529
1555
def all_revision_ids(self):
2530
path = self.controldir._path_for_remote_call(self._client)
2532
response_tuple, response_handler = self._call_expecting_body(
2533
b"Repository.all_revision_ids", path)
2534
except errors.UnknownSmartMethod:
2536
return self._real_repository.all_revision_ids()
2537
if response_tuple != (b"ok", ):
2538
raise errors.UnexpectedSmartServerResponse(response_tuple)
2539
revids = set(response_handler.read_body_bytes().splitlines())
2540
for fallback in self._fallback_repositories:
2541
revids.update(set(fallback.all_revision_ids()))
2544
def _filtered_revision_trees(self, revision_ids, file_ids):
2545
"""Return Tree for a revision on this branch with only some files.
2547
:param revision_ids: a sequence of revision-ids;
2548
a revision-id may not be None or b'null:'
2549
:param file_ids: if not None, the result is filtered
2550
so that only those file-ids, their parents and their
2551
children are included.
2553
inventories = self.iter_inventories(revision_ids)
2554
for inv in inventories:
2555
# Should we introduce a FilteredRevisionTree class rather
2556
# than pre-filter the inventory here?
2557
filtered_inv = inv.filter(file_ids)
2558
yield InventoryRevisionTree(self, filtered_inv, filtered_inv.revision_id)
2560
def get_revision_delta(self, revision_id):
2561
with self.lock_read():
2562
r = self.get_revision(revision_id)
2563
return list(self.get_revision_deltas([r]))[0]
1557
return self._real_repository.all_revision_ids()
1560
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)
2565
1572
def revision_trees(self, revision_ids):
2566
with self.lock_read():
2567
inventories = self.iter_inventories(revision_ids)
2568
for inv in inventories:
2569
yield RemoteInventoryTree(self, inv, inv.revision_id)
1574
return self._real_repository.revision_trees(revision_ids)
2571
1577
def get_revision_reconcile(self, revision_id):
2572
with self.lock_read():
2574
return self._real_repository.get_revision_reconcile(revision_id)
1579
return self._real_repository.get_revision_reconcile(revision_id)
2576
1582
def check(self, revision_ids=None, callback_refs=None, check_repo=True):
2577
with self.lock_read():
2579
return self._real_repository.check(revision_ids=revision_ids,
2580
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)
2582
1587
def copy_content_into(self, destination, revision_id=None):
2583
"""Make a complete copy of the content in self into destination.
2585
This is a destructive operation! Do not use it on existing
2588
interrepo = _mod_repository.InterRepository.get(self, destination)
2589
return interrepo.copy_content(revision_id)
1589
return self._real_repository.copy_content_into(
1590
destination, revision_id=revision_id)
2591
1592
def _copy_repository_tarball(self, to_bzrdir, revision_id=None):
2592
1593
# get a tarball of the remote repository, and copy from that into the
1595
from bzrlib import osutils
2595
1597
# TODO: Maybe a progress bar while streaming the tarball?
2596
note(gettext("Copying repository content as tarball..."))
1598
note("Copying repository content as tarball...")
2597
1599
tar_file = self._get_tarball('bz2')
2598
1600
if tar_file is None:
2600
1602
destination = to_bzrdir.create_repository()
2602
1604
tar = tarfile.open('repository', fileobj=tar_file,
2604
1606
tmpdir = osutils.mkdtemp()
2606
tar.extractall(tmpdir)
2607
tmp_bzrdir = _mod_bzrdir.BzrDir.open(tmpdir)
1608
_extract_tar(tar, tmpdir)
1609
tmp_bzrdir = BzrDir.open(tmpdir)
2608
1610
tmp_repo = tmp_bzrdir.open_repository()
2609
1611
tmp_repo.copy_content_into(destination, revision_id)
2698
1689
self._ensure_real()
2699
1690
return self._real_repository.texts
2701
def _iter_revisions_rpc(self, revision_ids):
2702
body = b"\n".join(revision_ids)
2703
path = self.controldir._path_for_remote_call(self._client)
2704
response_tuple, response_handler = (
2705
self._call_with_body_bytes_expecting_body(
2706
b"Repository.iter_revisions", (path, ), body))
2707
if response_tuple[0] != b"ok":
2708
raise errors.UnexpectedSmartServerResponse(response_tuple)
2709
serializer_format = response_tuple[1].decode('ascii')
2710
serializer = serializer_format_registry.get(serializer_format)
2711
byte_stream = response_handler.read_streamed_body()
2712
decompressor = zlib.decompressobj()
2714
for bytes in byte_stream:
2715
chunks.append(decompressor.decompress(bytes))
2716
if decompressor.unused_data != b"":
2717
chunks.append(decompressor.flush())
2718
yield serializer.read_revision_from_string(b"".join(chunks))
2719
unused = decompressor.unused_data
2720
decompressor = zlib.decompressobj()
2721
chunks = [decompressor.decompress(unused)]
2722
chunks.append(decompressor.flush())
2723
text = b"".join(chunks)
2725
yield serializer.read_revision_from_string(b"".join(chunks))
2727
def iter_revisions(self, revision_ids):
2728
for rev_id in revision_ids:
2729
if not rev_id or not isinstance(rev_id, bytes):
2730
raise errors.InvalidRevisionId(
2731
revision_id=rev_id, branch=self)
2732
with self.lock_read():
2734
missing = set(revision_ids)
2735
for rev in self._iter_revisions_rpc(revision_ids):
2736
missing.remove(rev.revision_id)
2737
yield (rev.revision_id, rev)
2738
for fallback in self._fallback_repositories:
2741
for (revid, rev) in fallback.iter_revisions(missing):
2744
missing.remove(revid)
2745
for revid in missing:
2747
except errors.UnknownSmartMethod:
2749
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)
2752
1697
def supports_rich_root(self):
2753
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)
2756
1705
def _serializer(self):
2757
1706
return self._format._serializer
2759
1708
def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
2760
with self.lock_write():
2761
signature = gpg_strategy.sign(plaintext, gpg.MODE_CLEAR)
2762
self.add_signature_text(revision_id, signature)
1710
return self._real_repository.store_revision_signature(
1711
gpg_strategy, plaintext, revision_id)
2764
1713
def add_signature_text(self, revision_id, signature):
2765
if self._real_repository:
2766
# If there is a real repository the write group will
2767
# be in the real repository as well, so use that:
2769
return self._real_repository.add_signature_text(
2770
revision_id, signature)
2771
path = self.controldir._path_for_remote_call(self._client)
2772
response, handler = self._call_with_body_bytes_expecting_body(
2773
b'Repository.add_signature_text', (path, self._lock_token,
2775
tuple([token.encode('utf-8')
2776
for token in self._write_group_tokens]),
2778
handler.cancel_read_body()
2780
if response[0] != b'ok':
2781
raise errors.UnexpectedSmartServerResponse(response)
2782
self._write_group_tokens = [token.decode(
2783
'utf-8') for token in response[1:]]
1715
return self._real_repository.add_signature_text(revision_id, signature)
2785
1717
def has_signature_for_revision_id(self, revision_id):
2786
path = self.controldir._path_for_remote_call(self._client)
2788
response = self._call(b'Repository.has_signature_for_revision_id',
2790
except errors.UnknownSmartMethod:
2792
return self._real_repository.has_signature_for_revision_id(
2794
if response[0] not in (b'yes', b'no'):
2795
raise SmartProtocolError(
2796
'unexpected response code %s' % (response,))
2797
if response[0] == b'yes':
2799
for fallback in self._fallback_repositories:
2800
if fallback.has_signature_for_revision_id(revision_id):
2804
def verify_revision_signature(self, revision_id, gpg_strategy):
2805
with self.lock_read():
2806
if not self.has_signature_for_revision_id(revision_id):
2807
return gpg.SIGNATURE_NOT_SIGNED, None
2808
signature = self.get_signature_text(revision_id)
2810
testament = _mod_testament.Testament.from_revision(
2813
(status, key, signed_plaintext) = gpg_strategy.verify(signature)
2814
if testament.as_short_text() != signed_plaintext:
2815
return gpg.SIGNATURE_NOT_VALID, None
2816
return (status, key)
1719
return self._real_repository.has_signature_for_revision_id(revision_id)
2818
1721
def item_keys_introduced_by(self, revision_ids, _files_pb=None):
2819
1722
self._ensure_real()
2820
1723
return self._real_repository.item_keys_introduced_by(revision_ids,
2821
_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()
2823
1731
def _find_inconsistent_revision_parents(self, revisions_iterator=None):
2824
1732
self._ensure_real()
2842
1751
:param recipe: A search recipe (start, stop, count).
2843
1752
:return: Serialised bytes.
2845
start_keys = b' '.join(recipe[1])
2846
stop_keys = b' '.join(recipe[2])
2847
count = str(recipe[3]).encode('ascii')
2848
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))
2850
1759
def _serialise_search_result(self, search_result):
2851
parts = search_result.get_network_struct()
2852
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)
2854
1768
def autopack(self):
2855
path = self.controldir._path_for_remote_call(self._client)
1769
path = self.bzrdir._path_for_remote_call(self._client)
2857
response = self._call(b'PackRepository.autopack', path)
1771
response = self._call('PackRepository.autopack', path)
2858
1772
except errors.UnknownSmartMethod:
2859
1773
self._ensure_real()
2860
1774
self._real_repository._pack_collection.autopack()
2862
1776
self.refresh_data()
2863
if response[0] != b'ok':
2864
raise errors.UnexpectedSmartServerResponse(response)
2866
def _revision_archive(self, revision_id, format, name, root, subdir,
2868
path = self.controldir._path_for_remote_call(self._client)
2869
format = format or ''
2871
subdir = subdir or ''
2872
force_mtime = int(force_mtime) if force_mtime is not None else None
2874
response, protocol = self._call_expecting_body(
2875
b'Repository.revision_archive', path,
2877
format.encode('ascii'),
2878
os.path.basename(name).encode('utf-8'),
2879
root.encode('utf-8'),
2880
subdir.encode('utf-8'),
2882
except errors.UnknownSmartMethod:
2884
if response[0] == b'ok':
2885
return iter([protocol.read_body_bytes()])
2886
raise errors.UnexpectedSmartServerResponse(response)
2888
def _annotate_file_revision(self, revid, tree_path, file_id, default_revision):
2889
path = self.controldir._path_for_remote_call(self._client)
2890
tree_path = tree_path.encode('utf-8')
2891
file_id = file_id or b''
2892
default_revision = default_revision or b''
2894
response, handler = self._call_expecting_body(
2895
b'Repository.annotate_file_revision', path,
2896
revid, tree_path, file_id, default_revision)
2897
except errors.UnknownSmartMethod:
2899
if response[0] != b'ok':
2900
raise errors.UnexpectedSmartServerResponse(response)
2901
return map(tuple, bencode.bdecode(handler.read_body_bytes()))
2904
class RemoteStreamSink(vf_repository.StreamSink):
1777
if response[0] != 'ok':
1778
raise errors.UnexpectedSmartServerResponse(response)
1781
class RemoteStreamSink(repository.StreamSink):
2906
1783
def _insert_real(self, stream, src_format, resume_tokens):
2907
1784
self.target_repo._ensure_real()
3251
2069
def network_name(self):
3252
2070
return self._network_name
3254
def open(self, a_controldir, name=None, ignore_fallbacks=False):
3255
return a_controldir.open_branch(name=name,
3256
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)
3258
def _vfs_initialize(self, a_controldir, name, append_revisions_only,
2076
def _vfs_initialize(self, a_bzrdir, name):
3260
2077
# Initialisation when using a local bzrdir object, or a non-vfs init
3261
2078
# method is not available on the server.
3262
2079
# self._custom_format is always set - the start of initialize ensures
3264
if isinstance(a_controldir, RemoteBzrDir):
3265
a_controldir._ensure_real()
3266
result = self._custom_format.initialize(a_controldir._real_bzrdir,
3267
name=name, append_revisions_only=append_revisions_only,
3268
repository=repository)
2081
if isinstance(a_bzrdir, RemoteBzrDir):
2082
a_bzrdir._ensure_real()
2083
result = self._custom_format.initialize(a_bzrdir._real_bzrdir,
3270
2086
# We assume the bzrdir is parameterised; it may not be.
3271
result = self._custom_format.initialize(a_controldir, name=name,
3272
append_revisions_only=append_revisions_only,
3273
repository=repository)
3274
if (isinstance(a_controldir, RemoteBzrDir)
3275
and not isinstance(result, RemoteBranch)):
3276
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,
3280
def initialize(self, a_controldir, name=None, repository=None,
3281
append_revisions_only=None):
3283
name = a_controldir._get_selected_branch()
2094
def initialize(self, a_bzrdir, name=None):
3284
2095
# 1) get the network name to use.
3285
2096
if self._custom_format:
3286
2097
network_name = self._custom_format.network_name()
3288
# Select the current breezy default and ask for that.
3289
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')()
3291
2101
reference_format = reference_bzrdir_format.get_branch_format()
3292
2102
self._custom_format = reference_format
3293
2103
network_name = reference_format.network_name()
3294
2104
# Being asked to create on a non RemoteBzrDir:
3295
if not isinstance(a_controldir, RemoteBzrDir):
3296
return self._vfs_initialize(a_controldir, name=name,
3297
append_revisions_only=append_revisions_only,
3298
repository=repository)
3299
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
3300
2108
if medium._is_remote_before((1, 13)):
3301
return self._vfs_initialize(a_controldir, name=name,
3302
append_revisions_only=append_revisions_only,
3303
repository=repository)
2109
return self._vfs_initialize(a_bzrdir, name=name)
3304
2110
# Creating on a remote bzr dir.
3305
2111
# 2) try direct creation via RPC
3306
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:
3308
2114
# XXX JRV20100304: Support creating colocated branches
3309
2115
raise errors.NoColocatedBranchSupport(self)
3310
verb = b'BzrDir.create_branch'
2116
verb = 'BzrDir.create_branch'
3312
response = a_controldir._call(verb, path, network_name)
2118
response = a_bzrdir._call(verb, path, network_name)
3313
2119
except errors.UnknownSmartMethod:
3314
2120
# Fallback - use vfs methods
3315
2121
medium._remember_remote_is_before((1, 13))
3316
return self._vfs_initialize(a_controldir, name=name,
3317
append_revisions_only=append_revisions_only,
3318
repository=repository)
3319
if response[0] != b'ok':
2122
return self._vfs_initialize(a_bzrdir, name=name)
2123
if response[0] != 'ok':
3320
2124
raise errors.UnexpectedSmartServerResponse(response)
3321
2125
# Turn the response into a RemoteRepository object.
3322
2126
format = RemoteBranchFormat(network_name=response[1])
3323
2127
repo_format = response_tuple_to_repo_format(response[3:])
3324
repo_path = response[2].decode('utf-8')
3325
if repository is not None:
3326
remote_repo_url = urlutils.join(a_controldir.user_url, repo_path)
3327
url_diff = urlutils.relative_url(repository.user_url,
3330
raise AssertionError(
3331
'repository.user_url %r does not match URL from server '
3332
'response (%r + %r)'
3333
% (repository.user_url, a_controldir.user_url, repo_path))
3334
remote_repo = repository
2128
if response[2] == '':
2129
repo_bzrdir = a_bzrdir
3337
repo_bzrdir = a_controldir
3339
repo_bzrdir = RemoteBzrDir(
3340
a_controldir.root_transport.clone(
3341
repo_path), a_controldir._format,
3342
a_controldir._client)
3343
remote_repo = RemoteRepository(repo_bzrdir, repo_format)
3344
remote_branch = RemoteBranch(a_controldir, remote_repo,
3345
format=format, setup_stacking=False, name=name)
3346
if append_revisions_only:
3347
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)
3348
2137
# XXX: We know this is a new branch, so it must have revno 0, revid
3349
2138
# NULL_REVISION. Creating the branch locked would make this be unable
3350
2139
# to be wrong; here its simply very unlikely to be wrong. RBC 20090225
3369
2158
self._ensure_real()
3370
2159
return self._custom_format.supports_set_append_revisions_only()
3373
def supports_reference_locations(self):
3375
return self._custom_format.supports_reference_locations
3377
def stores_revno(self):
3380
def _use_default_local_heads_to_fetch(self):
3381
# If the branch format is a metadir format *and* its heads_to_fetch
3382
# implementation is not overridden vs the base class, we can use the
3383
# base class logic rather than use the heads_to_fetch RPC. This is
3384
# usually cheaper in terms of net round trips, as the last-revision and
3385
# tags info fetched is cached and would be fetched anyway.
3387
if isinstance(self._custom_format, bzrbranch.BranchFormatMetadir):
3388
branch_class = self._custom_format._branch_class()
3389
heads_to_fetch_impl = branch_class.heads_to_fetch
3390
if heads_to_fetch_impl is branch.Branch.heads_to_fetch:
3395
class RemoteBranchStore(_mod_config.IniFileStore):
3396
"""Branch store which attempts to use HPSS calls to retrieve branch store.
3398
Note that this is specific to bzr-based formats.
3401
def __init__(self, branch):
3402
super(RemoteBranchStore, self).__init__()
3403
self.branch = branch
3405
self._real_store = None
3407
def external_url(self):
3408
return urlutils.join(self.branch.user_url, 'branch.conf')
3410
def _load_content(self):
3411
path = self.branch._remote_path()
3413
response, handler = self.branch._call_expecting_body(
3414
b'Branch.get_config_file', path)
3415
except errors.UnknownSmartMethod:
3417
return self._real_store._load_content()
3418
if len(response) and response[0] != b'ok':
3419
raise errors.UnexpectedSmartServerResponse(response)
3420
return handler.read_body_bytes()
3422
def _save_content(self, content):
3423
path = self.branch._remote_path()
3425
response, handler = self.branch._call_with_body_bytes_expecting_body(
3426
b'Branch.put_config_file', (path,
3427
self.branch._lock_token, self.branch._repo_lock_token),
3429
except errors.UnknownSmartMethod:
3431
return self._real_store._save_content(content)
3432
handler.cancel_read_body()
3433
if response != (b'ok', ):
3434
raise errors.UnexpectedSmartServerResponse(response)
3436
def _ensure_real(self):
3437
self.branch._ensure_real()
3438
if self._real_store is None:
3439
self._real_store = _mod_config.BranchStore(self.branch)
3442
2162
class RemoteBranch(branch.Branch, _RpcHelper, lock._RelockDebugMixin):
3443
2163
"""Branch stored on a server accessed by HPSS RPC.
3992
2666
self._ensure_real()
3993
2667
return self._real_branch._set_parent_location(url)
3995
2670
def pull(self, source, overwrite=False, stop_revision=None,
3997
with self.lock_write():
3998
self._clear_cached_state_of_remote_branch_only()
4000
return self._real_branch.pull(
4001
source, overwrite=overwrite, stop_revision=stop_revision,
4002
_override_hook_target=self, **kwargs)
4004
def push(self, target, overwrite=False, stop_revision=None, lossy=False, tag_selector=None):
4005
with self.lock_read():
4007
return self._real_branch.push(
4008
target, overwrite=overwrite, stop_revision=stop_revision, lossy=lossy,
4009
_override_hook_source_branch=self, tag_selector=tag_selector)
4011
def peek_lock_mode(self):
4012
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)
4014
2685
def is_locked(self):
4015
2686
return self._lock_count >= 1
4017
def revision_id_to_dotted_revno(self, revision_id):
4018
"""Given a revision id, return its dotted revno.
4020
:return: a tuple like (1,) or (400,1,3).
4022
with self.lock_read():
4024
response = self._call(b'Branch.revision_id_to_revno',
4025
self._remote_path(), revision_id)
4026
except errors.UnknownSmartMethod:
4028
return self._real_branch.revision_id_to_dotted_revno(revision_id)
4029
except errors.UnknownErrorFromSmartServer as e:
4030
# Deal with older versions of bzr/brz that didn't explicitly
4031
# wrap GhostRevisionsHaveNoRevno.
4032
if e.error_tuple[1] == b'GhostRevisionsHaveNoRevno':
4033
(revid, ghost_revid) = re.findall(b"{([^}]+)}", e.error_tuple[2])
4034
raise errors.GhostRevisionsHaveNoRevno(
4037
if response[0] == b'ok':
4038
return tuple([int(x) for x in response[1:]])
4040
raise errors.UnexpectedSmartServerResponse(response)
4042
2689
def revision_id_to_revno(self, revision_id):
4043
"""Given a revision id on the branch mainline, return its revno.
4047
with self.lock_read():
4049
response = self._call(b'Branch.revision_id_to_revno',
4050
self._remote_path(), revision_id)
4051
except errors.UnknownSmartMethod:
4053
return self._real_branch.revision_id_to_revno(revision_id)
4054
if response[0] == b'ok':
4055
if len(response) == 2:
4056
return int(response[1])
4057
raise NoSuchRevision(self, revision_id)
4059
raise errors.UnexpectedSmartServerResponse(response)
2691
return self._real_branch.revision_id_to_revno(revision_id)
4061
2694
def set_last_revision_info(self, revno, revision_id):
4062
with self.lock_write():
4063
# XXX: These should be returned by the set_last_revision_info verb
4064
old_revno, old_revid = self.last_revision_info()
4065
self._run_pre_change_branch_tip_hooks(revno, revision_id)
4066
if not revision_id or not isinstance(revision_id, bytes):
4067
raise errors.InvalidRevisionId(
4068
revision_id=revision_id, branch=self)
4070
response = self._call(b'Branch.set_last_revision_info',
4071
self._remote_path(), self._lock_token, self._repo_lock_token,
4072
str(revno).encode('ascii'), revision_id)
4073
except errors.UnknownSmartMethod:
4075
self._clear_cached_state_of_remote_branch_only()
4076
self._real_branch.set_last_revision_info(revno, revision_id)
4077
self._last_revision_info_cache = revno, revision_id
4079
if response == (b'ok',):
4080
self._clear_cached_state()
4081
self._last_revision_info_cache = revno, revision_id
4082
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
4083
# Update the _real_branch's cache too.
4084
if self._real_branch is not None:
4085
cache = self._last_revision_info_cache
4086
self._real_branch._last_revision_info_cache = cache
4088
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)
4090
2721
def generate_revision_history(self, revision_id, last_rev=None,
4091
2722
other_branch=None):
4092
with self.lock_write():
4093
medium = self._client._medium
4094
if not medium._is_remote_before((1, 6)):
4095
# Use a smart method for 1.6 and above servers
4097
self._set_last_revision_descendant(revision_id, other_branch,
4098
allow_diverged=True, allow_overwrite_descendant=True)
4100
except errors.UnknownSmartMethod:
4101
medium._remember_remote_is_before((1, 6))
4102
self._clear_cached_state_of_remote_branch_only()
4103
graph = self.repository.get_graph()
4104
(last_revno, last_revid) = self.last_revision_info()
4105
known_revision_ids = [
4106
(last_revid, last_revno),
4107
(_mod_revision.NULL_REVISION, 0),
4109
if last_rev is not None:
4110
if not graph.is_ancestor(last_rev, revision_id):
4111
# our previous tip is not merged into stop_revision
4112
raise errors.DivergedBranches(self, other_branch)
4113
revno = graph.find_distance_to_null(
4114
revision_id, known_revision_ids)
4115
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))
4117
2736
def set_push_location(self, location):
4118
self._set_config_location('push_location', location)
4120
def heads_to_fetch(self):
4121
if self._format._use_default_local_heads_to_fetch():
4122
# We recognise this format, and its heads-to-fetch implementation
4123
# is the default one (tip + tags). In this case it's cheaper to
4124
# just use the default implementation rather than a special RPC as
4125
# the tip and tags data is cached.
4126
return branch.Branch.heads_to_fetch(self)
4127
medium = self._client._medium
4128
if medium._is_remote_before((2, 4)):
4129
return self._vfs_heads_to_fetch()
4131
return self._rpc_heads_to_fetch()
4132
except errors.UnknownSmartMethod:
4133
medium._remember_remote_is_before((2, 4))
4134
return self._vfs_heads_to_fetch()
4136
def _rpc_heads_to_fetch(self):
4137
response = self._call(b'Branch.heads_to_fetch', self._remote_path())
4138
if len(response) != 2:
4139
raise errors.UnexpectedSmartServerResponse(response)
4140
must_fetch, if_present_fetch = response
4141
return set(must_fetch), set(if_present_fetch)
4143
def _vfs_heads_to_fetch(self):
4145
return self._real_branch.heads_to_fetch()
4147
def reconcile(self, thorough=True):
4148
"""Make sure the data stored in this branch is consistent."""
4149
from .reconcile import BranchReconciler
4150
with self.lock_write():
4151
reconciler = BranchReconciler(self, thorough=thorough)
4152
return reconciler.reconcile()
4154
def get_reference_info(self, file_id):
4155
"""Get the tree_path and branch_location for a tree reference."""
4156
if not self._format.supports_reference_locations:
4157
raise errors.UnsupportedOperation(self.get_reference_info, self)
4158
return self._get_all_reference_info().get(file_id, (None, None))
4160
def set_reference_info(self, file_id, branch_location, tree_path=None):
4161
"""Set the branch location to use for a tree reference."""
4162
if not self._format.supports_reference_locations:
4163
raise errors.UnsupportedOperation(self.set_reference_info, self)
4165
self._real_branch.set_reference_info(
4166
file_id, branch_location, tree_path)
4168
def _set_all_reference_info(self, reference_info):
4169
if not self._format.supports_reference_locations:
4170
raise errors.UnsupportedOperation(self.set_reference_info, self)
4172
self._real_branch._set_all_reference_info(reference_info)
4174
def _get_all_reference_info(self):
4175
if not self._format.supports_reference_locations:
4178
response, handler = self._call_expecting_body(
4179
b'Branch.get_all_reference_info', self._remote_path())
4180
except errors.UnknownSmartMethod:
4182
return self._real_branch._get_all_reference_info()
4183
if len(response) and response[0] != b'ok':
4184
raise errors.UnexpectedSmartServerResponse(response)
4186
for (f, u, p) in bencode.bdecode(handler.read_body_bytes()):
4187
ret[f] = (u.decode('utf-8'), p.decode('utf-8') if p else None)
4190
def reference_parent(self, file_id, path, possible_transports=None):
4191
"""Return the parent branch for a tree-reference.
4193
:param path: The path of the nested tree in the tree
4194
:return: A branch associated with the nested tree
4196
branch_location = self.get_reference_info(file_id)[0]
4197
if branch_location is None:
4199
return branch.Branch.open_from_transport(
4200
self.controldir.root_transport.clone(path),
4201
possible_transports=possible_transports)
4202
except errors.NotBranchError:
4204
return branch.Branch.open(
4206
urlutils.strip_segment_parameters(self.user_url), branch_location),
4207
possible_transports=possible_transports)
2738
return self._real_branch.set_push_location(location)
4210
2741
class RemoteConfig(object):
4392
2912
def find(name):
4394
2914
return context[name]
4396
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)
4399
2918
def get_path():
4400
2919
"""Get the path from the context if present, otherwise use first error
4404
2923
return context['path']
2924
except KeyError, key_err:
4407
return err.error_args[0].decode('utf-8')
4409
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)
4411
if not isinstance(err.error_verb, bytes):
4412
raise TypeError(err.error_verb)
4414
translator = error_translators.get(err.error_verb)
4418
raise translator(err, find, get_path)
4420
translator = no_context_error_translators.get(err.error_verb)
4422
raise errors.UnknownErrorFromSmartServer(err)
4424
raise translator(err)
4427
error_translators.register(b'NoSuchRevision',
4428
lambda err, find, get_path: NoSuchRevision(
4429
find('branch'), err.error_args[0]))
4430
error_translators.register(b'nosuchrevision',
4431
lambda err, find, get_path: NoSuchRevision(
4432
find('repository'), err.error_args[0]))
4433
error_translators.register(
4434
b'revno-outofbounds',
4435
lambda err, find, get_path: errors.RevnoOutOfBounds(
4436
err.error_args[0], (err.error_args[1], err.error_args[2])))
4439
def _translate_nobranch_error(err, find, get_path):
4440
if len(err.error_args) >= 1:
4441
extra = err.error_args[0].decode('utf-8')
4444
return errors.NotBranchError(path=find('bzrdir').root_transport.base,
4448
error_translators.register(b'nobranch', _translate_nobranch_error)
4449
error_translators.register(b'norepository',
4450
lambda err, find, get_path: errors.NoRepositoryPresent(
4452
error_translators.register(b'UnlockableTransport',
4453
lambda err, find, get_path: errors.UnlockableTransport(
4454
find('bzrdir').root_transport))
4455
error_translators.register(b'TokenMismatch',
4456
lambda err, find, get_path: errors.TokenMismatch(
4457
find('token'), '(remote token)'))
4458
error_translators.register(b'Diverged',
4459
lambda err, find, get_path: errors.DivergedBranches(
4460
find('branch'), find('other_branch')))
4461
error_translators.register(b'NotStacked',
4462
lambda err, find, get_path: errors.NotStacked(branch=find('branch')))
4465
def _translate_PermissionDenied(err, find, get_path):
4467
if len(err.error_args) >= 2:
4468
extra = err.error_args[1].decode('utf-8')
4471
return errors.PermissionDenied(path, extra=extra)
4474
error_translators.register(b'PermissionDenied', _translate_PermissionDenied)
4475
error_translators.register(b'ReadError',
4476
lambda err, find, get_path: errors.ReadError(get_path()))
4477
error_translators.register(b'NoSuchFile',
4478
lambda err, find, get_path: errors.NoSuchFile(get_path()))
4479
error_translators.register(b'TokenLockingNotSupported',
4480
lambda err, find, get_path: errors.TokenLockingNotSupported(
4481
find('repository')))
4482
error_translators.register(b'UnsuspendableWriteGroup',
4483
lambda err, find, get_path: errors.UnsuspendableWriteGroup(
4484
repository=find('repository')))
4485
error_translators.register(b'UnresumableWriteGroup',
4486
lambda err, find, get_path: errors.UnresumableWriteGroup(
4487
repository=find('repository'), write_groups=err.error_args[0],
4488
reason=err.error_args[1]))
4489
error_translators.register(b'AlreadyControlDir',
4490
lambda err, find, get_path: errors.AlreadyControlDirError(get_path()))
4492
no_context_error_translators.register(b'GhostRevisionsHaveNoRevno',
4493
lambda err: errors.GhostRevisionsHaveNoRevno(*err.error_args))
4494
no_context_error_translators.register(b'IncompatibleRepositories',
4495
lambda err: errors.IncompatibleRepositories(
4496
err.error_args[0].decode('utf-8'), err.error_args[1].decode('utf-8'), err.error_args[2].decode('utf-8')))
4497
no_context_error_translators.register(b'LockContention',
4498
lambda err: errors.LockContention('(remote lock)'))
4499
no_context_error_translators.register(b'LockFailed',
4500
lambda err: errors.LockFailed(err.error_args[0].decode('utf-8'), err.error_args[1].decode('utf-8')))
4501
no_context_error_translators.register(b'TipChangeRejected',
4502
lambda err: errors.TipChangeRejected(err.error_args[0].decode('utf8')))
4503
no_context_error_translators.register(b'UnstackableBranchFormat',
4504
lambda err: branch.UnstackableBranchFormat(*err.error_args))
4505
no_context_error_translators.register(b'UnstackableRepositoryFormat',
4506
lambda err: errors.UnstackableRepositoryFormat(*err.error_args))
4507
no_context_error_translators.register(b'FileExists',
4508
lambda err: errors.FileExists(err.error_args[0].decode('utf-8')))
4509
no_context_error_translators.register(b'DirectoryNotEmpty',
4510
lambda err: errors.DirectoryNotEmpty(err.error_args[0].decode('utf-8')))
4511
no_context_error_translators.register(b'UnknownFormat',
4512
lambda err: errors.UnknownFormatError(
4513
err.error_args[0].decode('ascii'), err.error_args[0].decode('ascii')))
4514
no_context_error_translators.register(b'InvalidURL',
4515
lambda err: urlutils.InvalidURL(
4516
err.error_args[0].decode('utf-8'), err.error_args[1].decode('ascii')))
4519
def _translate_short_readv_error(err):
4520
args = err.error_args
4521
return errors.ShortReadvError(
4522
args[0].decode('utf-8'),
4523
int(args[1].decode('ascii')), int(args[2].decode('ascii')),
4524
int(args[3].decode('ascii')))
4527
no_context_error_translators.register(b'ShortReadvError',
4528
_translate_short_readv_error)
4531
def _translate_unicode_error(err):
4532
encoding = err.error_args[0].decode('ascii')
4533
val = err.error_args[1].decode('utf-8')
4534
start = int(err.error_args[2].decode('ascii'))
4535
end = int(err.error_args[3].decode('ascii'))
4536
reason = err.error_args[4].decode('utf-8')
4537
if val.startswith('u:'):
4538
val = val[2:].decode('utf-8')
4539
elif val.startswith('s:'):
4540
val = val[2:].decode('base64')
4541
if err.error_verb == 'UnicodeDecodeError':
4542
raise UnicodeDecodeError(encoding, val, start, end, reason)
4543
elif err.error_verb == 'UnicodeEncodeError':
4544
raise UnicodeEncodeError(encoding, val, start, end, reason)
4547
no_context_error_translators.register(b'UnicodeEncodeError',
4548
_translate_unicode_error)
4549
no_context_error_translators.register(b'UnicodeDecodeError',
4550
_translate_unicode_error)
4551
no_context_error_translators.register(b'ReadOnlyError',
4552
lambda err: errors.TransportNotPossible('readonly transport'))
4553
no_context_error_translators.register(b'MemoryError',
4554
lambda err: errors.BzrError("remote server out of memory\n"
4555
"Retry non-remotely, or contact the server admin for details."))
4556
no_context_error_translators.register(b'RevisionNotPresent',
4557
lambda err: errors.RevisionNotPresent(err.error_args[0].decode('utf-8'), err.error_args[1].decode('utf-8')))
4559
no_context_error_translators.register(b'BzrCheckError',
4560
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)