89
# Note: RemoteBzrDirFormat is in bzrdir.py
91
class RemoteBzrDir(BzrDir, _RpcHelper):
116
# Note that RemoteBzrDirProber lives in breezy.bzrdir so breezy.bzr.remote
117
# does not have to be imported unless a remote format is involved.
119
class RemoteBzrDirFormat(_mod_bzrdir.BzrDirMetaFormat1):
120
"""Format representing bzrdirs accessed via a smart server"""
122
supports_workingtrees = False
124
colocated_branches = False
127
_mod_bzrdir.BzrDirMetaFormat1.__init__(self)
128
# XXX: It's a bit ugly that the network name is here, because we'd
129
# like to believe that format objects are stateless or at least
130
# immutable, However, we do at least avoid mutating the name after
131
# it's returned. See <https://bugs.launchpad.net/bzr/+bug/504102>
132
self._network_name = None
135
return "%s(_network_name=%r)" % (self.__class__.__name__,
138
def get_format_description(self):
139
if self._network_name:
141
real_format = controldir.network_format_registry.get(
146
return 'Remote: ' + real_format.get_format_description()
147
return 'bzr remote bzrdir'
149
def get_format_string(self):
150
raise NotImplementedError(self.get_format_string)
152
def network_name(self):
153
if self._network_name:
154
return self._network_name
156
raise AssertionError("No network name set.")
158
def initialize_on_transport(self, transport):
160
# hand off the request to the smart server
161
client_medium = transport.get_smart_medium()
162
except errors.NoSmartMedium:
163
# TODO: lookup the local format from a server hint.
164
local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
165
return local_dir_format.initialize_on_transport(transport)
166
client = _SmartClient(client_medium)
167
path = client.remote_path_from_transport(transport)
169
response = client.call('BzrDirFormat.initialize', path)
170
except errors.ErrorFromSmartServer as err:
171
_translate_error(err, path=path)
172
if response[0] != 'ok':
173
raise errors.SmartProtocolError('unexpected response code %s' % (response,))
174
format = RemoteBzrDirFormat()
175
self._supply_sub_formats_to(format)
176
return RemoteBzrDir(transport, format)
178
def parse_NoneTrueFalse(self, arg):
185
raise AssertionError("invalid arg %r" % arg)
187
def _serialize_NoneTrueFalse(self, arg):
194
def _serialize_NoneString(self, arg):
197
def initialize_on_transport_ex(self, transport, use_existing_dir=False,
198
create_prefix=False, force_new_repo=False, stacked_on=None,
199
stack_on_pwd=None, repo_format_name=None, make_working_trees=None,
202
# hand off the request to the smart server
203
client_medium = transport.get_smart_medium()
204
except errors.NoSmartMedium:
207
# Decline to open it if the server doesn't support our required
208
# version (3) so that the VFS-based transport will do it.
209
if client_medium.should_probe():
211
server_version = client_medium.protocol_version()
212
if server_version != '2':
216
except errors.SmartProtocolError:
217
# Apparently there's no usable smart server there, even though
218
# the medium supports the smart protocol.
223
client = _SmartClient(client_medium)
224
path = client.remote_path_from_transport(transport)
225
if client_medium._is_remote_before((1, 16)):
228
# TODO: lookup the local format from a server hint.
229
local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
230
self._supply_sub_formats_to(local_dir_format)
231
return local_dir_format.initialize_on_transport_ex(transport,
232
use_existing_dir=use_existing_dir, create_prefix=create_prefix,
233
force_new_repo=force_new_repo, stacked_on=stacked_on,
234
stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
235
make_working_trees=make_working_trees, shared_repo=shared_repo,
237
return self._initialize_on_transport_ex_rpc(client, path, transport,
238
use_existing_dir, create_prefix, force_new_repo, stacked_on,
239
stack_on_pwd, repo_format_name, make_working_trees, shared_repo)
241
def _initialize_on_transport_ex_rpc(self, client, path, transport,
242
use_existing_dir, create_prefix, force_new_repo, stacked_on,
243
stack_on_pwd, repo_format_name, make_working_trees, shared_repo):
245
args.append(self._serialize_NoneTrueFalse(use_existing_dir))
246
args.append(self._serialize_NoneTrueFalse(create_prefix))
247
args.append(self._serialize_NoneTrueFalse(force_new_repo))
248
args.append(self._serialize_NoneString(stacked_on))
249
# stack_on_pwd is often/usually our transport
252
stack_on_pwd = transport.relpath(stack_on_pwd)
255
except errors.PathNotChild:
257
args.append(self._serialize_NoneString(stack_on_pwd))
258
args.append(self._serialize_NoneString(repo_format_name))
259
args.append(self._serialize_NoneTrueFalse(make_working_trees))
260
args.append(self._serialize_NoneTrueFalse(shared_repo))
261
request_network_name = self._network_name or \
262
_mod_bzrdir.BzrDirFormat.get_default_format().network_name()
264
response = client.call('BzrDirFormat.initialize_ex_1.16',
265
request_network_name, path, *args)
266
except errors.UnknownSmartMethod:
267
client._medium._remember_remote_is_before((1,16))
268
local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
269
self._supply_sub_formats_to(local_dir_format)
270
return local_dir_format.initialize_on_transport_ex(transport,
271
use_existing_dir=use_existing_dir, create_prefix=create_prefix,
272
force_new_repo=force_new_repo, stacked_on=stacked_on,
273
stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
274
make_working_trees=make_working_trees, shared_repo=shared_repo,
276
except errors.ErrorFromSmartServer as err:
277
_translate_error(err, path=path)
278
repo_path = response[0]
279
bzrdir_name = response[6]
280
require_stacking = response[7]
281
require_stacking = self.parse_NoneTrueFalse(require_stacking)
282
format = RemoteBzrDirFormat()
283
format._network_name = bzrdir_name
284
self._supply_sub_formats_to(format)
285
bzrdir = RemoteBzrDir(transport, format, _client=client)
287
repo_format = response_tuple_to_repo_format(response[1:])
291
repo_bzrdir_format = RemoteBzrDirFormat()
292
repo_bzrdir_format._network_name = response[5]
293
repo_bzr = RemoteBzrDir(transport.clone(repo_path),
297
final_stack = response[8] or None
298
final_stack_pwd = response[9] or None
300
final_stack_pwd = urlutils.join(
301
transport.base, final_stack_pwd)
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, final_stack,
312
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()
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
'BzrDir.get_config_file', path)
403
except errors.UnknownSmartMethod:
405
return self._real_store._load_content()
406
if len(response) and response[0] != '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):
92
419
"""Control directory on a remote server, accessed via bzr:// or similar."""
94
421
def __init__(self, transport, format, _client=None, _force_probe=False):
266
664
def destroy_branch(self, name=None):
267
665
"""See BzrDir.destroy_branch"""
269
self._real_bzrdir.destroy_branch(name=name)
667
name = self._get_selected_branch()
669
raise errors.NoColocatedBranchSupport(self)
670
path = self._path_for_remote_call(self._client)
676
response = self._call('BzrDir.destroy_branch', path, *args)
677
except errors.UnknownSmartMethod:
679
self._real_bzrdir.destroy_branch(name=name)
680
self._next_open_branch_result = None
270
682
self._next_open_branch_result = None
683
if response[0] != 'ok':
684
raise SmartProtocolError('unexpected response code %s' % (response,))
272
def create_workingtree(self, revision_id=None, from_branch=None):
686
def create_workingtree(self, revision_id=None, from_branch=None,
687
accelerator_tree=None, hardlink=False):
273
688
raise errors.NotLocalUrl(self.transport.base)
275
def find_branch_format(self):
690
def find_branch_format(self, name=None):
276
691
"""Find the branch 'format' for this bzrdir.
278
693
This might be a synthetic object for e.g. RemoteBranch and SVN.
280
b = self.open_branch()
695
b = self.open_branch(name=name)
283
def get_branch_reference(self):
698
def get_branches(self, possible_transports=None, ignore_fallbacks=False):
699
path = self._path_for_remote_call(self._client)
701
response, handler = self._call_expecting_body(
702
'BzrDir.get_branches', path)
703
except errors.UnknownSmartMethod:
705
return self._real_bzrdir.get_branches()
706
if response[0] != "success":
707
raise errors.UnexpectedSmartServerResponse(response)
708
body = bencode.bdecode(handler.read_body_bytes())
710
for name, value in viewitems(body):
711
ret[name] = self._open_branch(name, value[0], value[1],
712
possible_transports=possible_transports,
713
ignore_fallbacks=ignore_fallbacks)
716
def set_branch_reference(self, target_branch, name=None):
717
"""See BzrDir.set_branch_reference()."""
719
name = self._get_selected_branch()
721
raise errors.NoColocatedBranchSupport(self)
723
return self._real_bzrdir.set_branch_reference(target_branch, name=name)
725
def get_branch_reference(self, name=None):
284
726
"""See BzrDir.get_branch_reference()."""
728
name = self._get_selected_branch()
730
raise errors.NoColocatedBranchSupport(self)
285
731
response = self._get_branch_reference()
286
732
if response[0] == 'ref':
287
733
return response[1]
318
764
raise errors.UnexpectedSmartServerResponse(response)
321
def _get_tree_branch(self):
767
def _get_tree_branch(self, name=None):
322
768
"""See BzrDir._get_tree_branch()."""
323
return None, self.open_branch()
769
return None, self.open_branch(name=name)
325
def open_branch(self, name=None, unsupported=False,
326
ignore_fallbacks=False):
328
raise NotImplementedError('unsupported flag support not implemented yet.')
329
if self._next_open_branch_result is not None:
330
# See create_branch for details.
331
result = self._next_open_branch_result
332
self._next_open_branch_result = None
334
response = self._get_branch_reference()
335
if response[0] == 'ref':
771
def _open_branch(self, name, kind, location_or_format,
772
ignore_fallbacks=False, possible_transports=None):
336
774
# a branch reference, use the existing BranchReference logic.
337
775
format = BranchReferenceFormat()
338
776
return format.open(self, name=name, _found=True,
339
location=response[1], ignore_fallbacks=ignore_fallbacks)
340
branch_format_name = response[1]
777
location=location_or_format, ignore_fallbacks=ignore_fallbacks,
778
possible_transports=possible_transports)
779
branch_format_name = location_or_format
341
780
if not branch_format_name:
342
781
branch_format_name = None
343
782
format = RemoteBranchFormat(network_name=branch_format_name)
344
783
return RemoteBranch(self, self.find_repository(), format=format,
345
setup_stacking=not ignore_fallbacks, name=name)
784
setup_stacking=not ignore_fallbacks, name=name,
785
possible_transports=possible_transports)
787
def open_branch(self, name=None, unsupported=False,
788
ignore_fallbacks=False, possible_transports=None):
790
name = self._get_selected_branch()
792
raise errors.NoColocatedBranchSupport(self)
794
raise NotImplementedError('unsupported flag support not implemented yet.')
795
if self._next_open_branch_result is not None:
796
# See create_branch for details.
797
result = self._next_open_branch_result
798
self._next_open_branch_result = None
800
response = self._get_branch_reference()
801
return self._open_branch(name, response[0], response[1],
802
possible_transports=possible_transports,
803
ignore_fallbacks=ignore_fallbacks)
347
805
def _open_repo_v1(self, path):
348
806
verb = 'BzrDir.find_repository'
529
1011
self._custom_format.supports_tree_reference
530
1012
return self._supports_tree_reference
532
def _vfs_initialize(self, a_bzrdir, shared):
1015
def revision_graph_can_have_wrong_parents(self):
1016
if self._revision_graph_can_have_wrong_parents is None:
1018
self._revision_graph_can_have_wrong_parents = \
1019
self._custom_format.revision_graph_can_have_wrong_parents
1020
return self._revision_graph_can_have_wrong_parents
1022
def _vfs_initialize(self, a_controldir, shared):
533
1023
"""Helper for common code in initialize."""
534
1024
if self._custom_format:
535
1025
# Custom format requested
536
result = self._custom_format.initialize(a_bzrdir, shared=shared)
1026
result = self._custom_format.initialize(a_controldir, shared=shared)
537
1027
elif self._creating_bzrdir is not None:
538
1028
# Use the format that the repository we were created to back
540
1030
prior_repo = self._creating_bzrdir.open_repository()
541
1031
prior_repo._ensure_real()
542
1032
result = prior_repo._real_repository._format.initialize(
543
a_bzrdir, shared=shared)
1033
a_controldir, shared=shared)
545
1035
# assume that a_bzr is a RemoteBzrDir but the smart server didn't
546
1036
# support remote initialization.
547
1037
# We delegate to a real object at this point (as RemoteBzrDir
548
1038
# delegate to the repository format which would lead to infinite
549
# recursion if we just called a_bzrdir.create_repository.
550
a_bzrdir._ensure_real()
551
result = a_bzrdir._real_bzrdir.create_repository(shared=shared)
1039
# recursion if we just called a_controldir.create_repository.
1040
a_controldir._ensure_real()
1041
result = a_controldir._real_bzrdir.create_repository(shared=shared)
552
1042
if not isinstance(result, RemoteRepository):
553
return self.open(a_bzrdir)
1043
return self.open(a_controldir)
557
def initialize(self, a_bzrdir, shared=False):
1047
def initialize(self, a_controldir, shared=False):
558
1048
# Being asked to create on a non RemoteBzrDir:
559
if not isinstance(a_bzrdir, RemoteBzrDir):
560
return self._vfs_initialize(a_bzrdir, shared)
561
medium = a_bzrdir._client._medium
1049
if not isinstance(a_controldir, RemoteBzrDir):
1050
return self._vfs_initialize(a_controldir, shared)
1051
medium = a_controldir._client._medium
562
1052
if medium._is_remote_before((1, 13)):
563
return self._vfs_initialize(a_bzrdir, shared)
1053
return self._vfs_initialize(a_controldir, shared)
564
1054
# Creating on a remote bzr dir.
565
1055
# 1) get the network name to use.
566
1056
if self._custom_format:
568
1058
elif self._network_name:
569
1059
network_name = self._network_name
571
# Select the current bzrlib default and ask for that.
572
reference_bzrdir_format = bzrdir.format_registry.get('default')()
1061
# Select the current breezy default and ask for that.
1062
reference_bzrdir_format = controldir.format_registry.get('default')()
573
1063
reference_format = reference_bzrdir_format.repository_format
574
1064
network_name = reference_format.network_name()
575
1065
# 2) try direct creation via RPC
576
path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
1066
path = a_controldir._path_for_remote_call(a_controldir._client)
577
1067
verb = 'BzrDir.create_repository'
579
1069
shared_str = 'True'
581
1071
shared_str = 'False'
583
response = a_bzrdir._call(verb, path, network_name, shared_str)
1073
response = a_controldir._call(verb, path, network_name, shared_str)
584
1074
except errors.UnknownSmartMethod:
585
1075
# Fallback - use vfs methods
586
1076
medium._remember_remote_is_before((1, 13))
587
return self._vfs_initialize(a_bzrdir, shared)
1077
return self._vfs_initialize(a_controldir, shared)
589
1079
# Turn the response into a RemoteRepository object.
590
1080
format = response_tuple_to_repo_format(response[1:])
591
1081
# Used to support creating a real format instance when needed.
592
format._creating_bzrdir = a_bzrdir
593
remote_repo = RemoteRepository(a_bzrdir, format)
1082
format._creating_bzrdir = a_controldir
1083
remote_repo = RemoteRepository(a_controldir, format)
594
1084
format._creating_repo = remote_repo
595
1085
return remote_repo
597
def open(self, a_bzrdir):
598
if not isinstance(a_bzrdir, RemoteBzrDir):
599
raise AssertionError('%r is not a RemoteBzrDir' % (a_bzrdir,))
600
return a_bzrdir.open_repository()
1087
def open(self, a_controldir):
1088
if not isinstance(a_controldir, RemoteBzrDir):
1089
raise AssertionError('%r is not a RemoteBzrDir' % (a_controldir,))
1090
return a_controldir.open_repository()
602
1092
def _ensure_real(self):
603
1093
if self._custom_format is None:
604
self._custom_format = repository.network_format_registry.get(
1095
self._custom_format = _mod_repository.network_format_registry.get(
1098
raise errors.UnknownFormatError(kind='repository',
1099
format=self._network_name)
608
1102
def _fetch_order(self):
1195
1776
raise errors.UnexpectedSmartServerResponse(response)
1197
1779
def sprout(self, to_bzrdir, revision_id=None):
1198
# TODO: Option to control what format is created?
1200
dest_repo = self._real_repository._format.initialize(to_bzrdir,
1780
"""Create a descendent repository for new development.
1782
Unlike clone, this does not copy the settings of the repository.
1784
dest_repo = self._create_sprouting_repo(to_bzrdir, shared=False)
1202
1785
dest_repo.fetch(self, revision_id=revision_id)
1203
1786
return dest_repo
1788
def _create_sprouting_repo(self, a_controldir, shared):
1789
if not isinstance(a_controldir._format, self.controldir._format.__class__):
1790
# use target default format.
1791
dest_repo = a_controldir.create_repository()
1793
# Most control formats need the repository to be specifically
1794
# created, but on some old all-in-one formats it's not needed
1796
dest_repo = self._format.initialize(a_controldir, shared=shared)
1797
except errors.UninitializableFormat:
1798
dest_repo = a_controldir.open_repository()
1205
1801
### These methods are just thin shims to the VFS object for now.
1207
1804
def revision_tree(self, revision_id):
1209
return self._real_repository.revision_tree(revision_id)
1805
revision_id = _mod_revision.ensure_null(revision_id)
1806
if revision_id == _mod_revision.NULL_REVISION:
1807
return InventoryRevisionTree(self,
1808
Inventory(root_id=None), _mod_revision.NULL_REVISION)
1810
return list(self.revision_trees([revision_id]))[0]
1211
1812
def get_serializer_format(self):
1213
return self._real_repository.get_serializer_format()
1813
path = self.controldir._path_for_remote_call(self._client)
1815
response = self._call('VersionedFileRepository.get_serializer_format',
1817
except errors.UnknownSmartMethod:
1819
return self._real_repository.get_serializer_format()
1820
if response[0] != 'ok':
1821
raise errors.UnexpectedSmartServerResponse(response)
1215
1824
def get_commit_builder(self, branch, parents, config, timestamp=None,
1216
1825
timezone=None, committer=None, revprops=None,
1218
# FIXME: It ought to be possible to call this without immediately
1219
# triggering _ensure_real. For now it's the easiest thing to do.
1221
real_repo = self._real_repository
1222
builder = real_repo.get_commit_builder(branch, parents,
1223
config, timestamp=timestamp, timezone=timezone,
1224
committer=committer, revprops=revprops, revision_id=revision_id)
1826
revision_id=None, lossy=False):
1827
"""Obtain a CommitBuilder for this repository.
1829
:param branch: Branch to commit to.
1830
:param parents: Revision ids of the parents of the new revision.
1831
:param config: Configuration to use.
1832
:param timestamp: Optional timestamp recorded for commit.
1833
:param timezone: Optional timezone for timestamp.
1834
:param committer: Optional committer to set for commit.
1835
:param revprops: Optional dictionary of revision properties.
1836
:param revision_id: Optional revision id.
1837
:param lossy: Whether to discard data that can not be natively
1838
represented, when pushing to a foreign VCS
1840
if self._fallback_repositories and not self._format.supports_chks:
1841
raise errors.BzrError("Cannot commit directly to a stacked branch"
1842
" in pre-2a formats. See "
1843
"https://bugs.launchpad.net/bzr/+bug/375013 for details.")
1844
if self._format.rich_root_data:
1845
commit_builder_kls = vf_repository.VersionedFileRootCommitBuilder
1847
commit_builder_kls = vf_repository.VersionedFileCommitBuilder
1848
result = commit_builder_kls(self, parents, config,
1849
timestamp, timezone, committer, revprops, revision_id,
1851
self.start_write_group()
1227
1854
def add_fallback_repository(self, repository):
1228
1855
"""Add a repository to use for looking up data not held locally.
1272
1900
delta, new_revision_id, parents, basis_inv=basis_inv,
1273
1901
propagate_caches=propagate_caches)
1275
def add_revision(self, rev_id, rev, inv=None, config=None):
1277
return self._real_repository.add_revision(
1278
rev_id, rev, inv=inv, config=config)
1903
def add_revision(self, revision_id, rev, inv=None):
1904
_mod_revision.check_not_reserved_id(revision_id)
1905
key = (revision_id,)
1906
# check inventory present
1907
if not self.inventories.get_parent_map([key]):
1909
raise errors.WeaveRevisionNotPresent(revision_id,
1912
# yes, this is not suitable for adding with ghosts.
1913
rev.inventory_sha1 = self.add_inventory(revision_id, inv,
1916
rev.inventory_sha1 = self.inventories.get_sha1s([key])[key]
1917
self._add_revision(rev)
1919
def _add_revision(self, rev):
1920
if self._real_repository is not None:
1921
return self._real_repository._add_revision(rev)
1922
text = self._serializer.write_revision_to_string(rev)
1923
key = (rev.revision_id,)
1924
parents = tuple((parent,) for parent in rev.parent_ids)
1925
self._write_group_tokens, missing_keys = self._get_sink().insert_stream(
1926
[('revisions', [FulltextContentFactory(key, parents, None, text)])],
1927
self._format, self._write_group_tokens)
1280
1929
@needs_read_lock
1281
1930
def get_inventory(self, revision_id):
1931
return list(self.iter_inventories([revision_id]))[0]
1933
def _iter_inventories_rpc(self, revision_ids, ordering):
1934
if ordering is None:
1935
ordering = 'unordered'
1936
path = self.controldir._path_for_remote_call(self._client)
1937
body = "\n".join(revision_ids)
1938
response_tuple, response_handler = (
1939
self._call_with_body_bytes_expecting_body(
1940
"VersionedFileRepository.get_inventories",
1941
(path, ordering), body))
1942
if response_tuple[0] != "ok":
1943
raise errors.UnexpectedSmartServerResponse(response_tuple)
1944
deserializer = inventory_delta.InventoryDeltaDeserializer()
1945
byte_stream = response_handler.read_streamed_body()
1946
decoded = smart_repo._byte_stream_to_stream(byte_stream)
1948
# no results whatsoever
1950
src_format, stream = decoded
1951
if src_format.network_name() != self._format.network_name():
1952
raise AssertionError(
1953
"Mismatched RemoteRepository and stream src %r, %r" % (
1954
src_format.network_name(), self._format.network_name()))
1955
# ignore the src format, it's not really relevant
1956
prev_inv = Inventory(root_id=None,
1957
revision_id=_mod_revision.NULL_REVISION)
1958
# there should be just one substream, with inventory deltas
1959
substream_kind, substream = next(stream)
1960
if substream_kind != "inventory-deltas":
1961
raise AssertionError(
1962
"Unexpected stream %r received" % substream_kind)
1963
for record in substream:
1964
(parent_id, new_id, versioned_root, tree_references, invdelta) = (
1965
deserializer.parse_text_bytes(record.get_bytes_as("fulltext")))
1966
if parent_id != prev_inv.revision_id:
1967
raise AssertionError("invalid base %r != %r" % (parent_id,
1968
prev_inv.revision_id))
1969
inv = prev_inv.create_by_apply_delta(invdelta, new_id)
1970
yield inv, inv.revision_id
1973
def _iter_inventories_vfs(self, revision_ids, ordering=None):
1282
1974
self._ensure_real()
1283
return self._real_repository.get_inventory(revision_id)
1975
return self._real_repository._iter_inventories(revision_ids, ordering)
1285
1977
def iter_inventories(self, revision_ids, ordering=None):
1287
return self._real_repository.iter_inventories(revision_ids, ordering)
1978
"""Get many inventories by revision_ids.
1980
This will buffer some or all of the texts used in constructing the
1981
inventories in memory, but will only parse a single inventory at a
1984
:param revision_ids: The expected revision ids of the inventories.
1985
:param ordering: optional ordering, e.g. 'topological'. If not
1986
specified, the order of revision_ids will be preserved (by
1987
buffering if necessary).
1988
:return: An iterator of inventories.
1990
if ((None in revision_ids)
1991
or (_mod_revision.NULL_REVISION in revision_ids)):
1992
raise ValueError('cannot get null revision inventory')
1993
for inv, revid in self._iter_inventories(revision_ids, ordering):
1995
raise errors.NoSuchRevision(self, revid)
1998
def _iter_inventories(self, revision_ids, ordering=None):
1999
if len(revision_ids) == 0:
2001
missing = set(revision_ids)
2002
if ordering is None:
2003
order_as_requested = True
2005
order = list(revision_ids)
2007
next_revid = order.pop()
2009
order_as_requested = False
2010
if ordering != 'unordered' and self._fallback_repositories:
2011
raise ValueError('unsupported ordering %r' % ordering)
2012
iter_inv_fns = [self._iter_inventories_rpc] + [
2013
fallback._iter_inventories for fallback in
2014
self._fallback_repositories]
2016
for iter_inv in iter_inv_fns:
2017
request = [revid for revid in revision_ids if revid in missing]
2018
for inv, revid in iter_inv(request, ordering):
2021
missing.remove(inv.revision_id)
2022
if ordering != 'unordered':
2026
if order_as_requested:
2027
# Yield as many results as we can while preserving order.
2028
while next_revid in invs:
2029
inv = invs.pop(next_revid)
2030
yield inv, inv.revision_id
2032
next_revid = order.pop()
2034
# We still want to fully consume the stream, just
2035
# in case it is not actually finished at this point
2038
except errors.UnknownSmartMethod:
2039
for inv, revid in self._iter_inventories_vfs(revision_ids, ordering):
2043
if order_as_requested:
2044
if next_revid is not None:
2045
yield None, next_revid
2048
yield invs.get(revid), revid
2051
yield None, missing.pop()
1289
2053
@needs_read_lock
1290
2054
def get_revision(self, revision_id):
1292
return self._real_repository.get_revision(revision_id)
2055
return self.get_revisions([revision_id])[0]
1294
2057
def get_transaction(self):
1295
2058
self._ensure_real()
1296
2059
return self._real_repository.get_transaction()
1298
2061
@needs_read_lock
1299
def clone(self, a_bzrdir, revision_id=None):
1301
return self._real_repository.clone(a_bzrdir, revision_id=revision_id)
2062
def clone(self, a_controldir, revision_id=None):
2063
dest_repo = self._create_sprouting_repo(
2064
a_controldir, shared=self.is_shared())
2065
self.copy_content_into(dest_repo, revision_id)
1303
2068
def make_working_trees(self):
1304
2069
"""See Repository.make_working_trees"""
1306
return self._real_repository.make_working_trees()
2070
path = self.controldir._path_for_remote_call(self._client)
2072
response = self._call('Repository.make_working_trees', path)
2073
except errors.UnknownSmartMethod:
2075
return self._real_repository.make_working_trees()
2076
if response[0] not in ('yes', 'no'):
2077
raise SmartProtocolError('unexpected response code %s' % (response,))
2078
return response[0] == 'yes'
1308
2080
def refresh_data(self):
1309
"""Re-read any data needed to to synchronise with disk.
2081
"""Re-read any data needed to synchronise with disk.
1311
2083
This method is intended to be called after another repository instance
1312
2084
(such as one used by a smart server) has inserted data into the
1313
repository. It may not be called during a write group, but may be
1314
called at any other time.
2085
repository. On all repositories this will work outside of write groups.
2086
Some repository formats (pack and newer for breezy native formats)
2087
support refresh_data inside write groups. If called inside a write
2088
group on a repository that does not support refreshing in a write group
2089
IsInWriteGroupError will be raised.
1316
if self.is_in_write_group():
1317
raise errors.InternalBzrError(
1318
"May not refresh_data while in a write group.")
1319
2091
if self._real_repository is not None:
1320
2092
self._real_repository.refresh_data()
2093
# Refresh the parents cache for this object
2094
self._unstacked_provider.disable_cache()
2095
self._unstacked_provider.enable_cache()
1322
2097
def revision_ids_to_search_result(self, result_set):
1323
2098
"""Convert a set of revision ids to a graph SearchResult."""
1324
2099
result_parents = set()
1325
for parents in self.get_graph().get_parent_map(
1326
result_set).itervalues():
2100
for parents in viewvalues(self.get_graph().get_parent_map(result_set)):
1327
2101
result_parents.update(parents)
1328
2102
included_keys = result_set.intersection(result_parents)
1329
2103
start_keys = result_set.difference(included_keys)
1330
2104
exclude_keys = result_parents.difference(result_set)
1331
result = graph.SearchResult(start_keys, exclude_keys,
2105
result = vf_search.SearchResult(start_keys, exclude_keys,
1332
2106
len(result_set), result_set)
1335
2109
@needs_read_lock
1336
def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
2110
def search_missing_revision_ids(self, other,
2111
find_ghosts=True, revision_ids=None, if_present_ids=None,
1337
2113
"""Return the revision ids that other has that this does not.
1339
2115
These are returned in topological order.
1341
2117
revision_id: only return revision ids included by revision_id.
1343
return repository.InterRepository.get(
1344
other, self).search_missing_revision_ids(revision_id, find_ghosts)
2119
inter_repo = _mod_repository.InterRepository.get(other, self)
2120
return inter_repo.search_missing_revision_ids(
2121
find_ghosts=find_ghosts, revision_ids=revision_ids,
2122
if_present_ids=if_present_ids, limit=limit)
1346
def fetch(self, source, revision_id=None, pb=None, find_ghosts=False,
2124
def fetch(self, source, revision_id=None, find_ghosts=False,
1347
2125
fetch_spec=None):
1348
2126
# No base implementation to use as RemoteRepository is not a subclass
1349
2127
# of Repository; so this is a copy of Repository.fetch().
1388
2165
return self._real_repository._get_versioned_file_checker(
1389
2166
revisions, revision_versions_cache)
2168
def _iter_files_bytes_rpc(self, desired_files, absent):
2169
path = self.controldir._path_for_remote_call(self._client)
2172
for (file_id, revid, identifier) in desired_files:
2173
lines.append("%s\0%s" % (
2174
osutils.safe_file_id(file_id),
2175
osutils.safe_revision_id(revid)))
2176
identifiers.append(identifier)
2177
(response_tuple, response_handler) = (
2178
self._call_with_body_bytes_expecting_body(
2179
"Repository.iter_files_bytes", (path, ), "\n".join(lines)))
2180
if response_tuple != ('ok', ):
2181
response_handler.cancel_read_body()
2182
raise errors.UnexpectedSmartServerResponse(response_tuple)
2183
byte_stream = response_handler.read_streamed_body()
2184
def decompress_stream(start, byte_stream, unused):
2185
decompressor = zlib.decompressobj()
2186
yield decompressor.decompress(start)
2187
while decompressor.unused_data == "":
2189
data = next(byte_stream)
2190
except StopIteration:
2192
yield decompressor.decompress(data)
2193
yield decompressor.flush()
2194
unused.append(decompressor.unused_data)
2197
while not "\n" in unused:
2198
unused += next(byte_stream)
2199
header, rest = unused.split("\n", 1)
2200
args = header.split("\0")
2201
if args[0] == "absent":
2202
absent[identifiers[int(args[3])]] = (args[1], args[2])
2205
elif args[0] == "ok":
2208
raise errors.UnexpectedSmartServerResponse(args)
2210
yield (identifiers[idx],
2211
decompress_stream(rest, byte_stream, unused_chunks))
2212
unused = "".join(unused_chunks)
1391
2214
def iter_files_bytes(self, desired_files):
1392
2215
"""See Repository.iter_file_bytes.
1395
return self._real_repository.iter_files_bytes(desired_files)
2219
for (identifier, bytes_iterator) in self._iter_files_bytes_rpc(
2220
desired_files, absent):
2221
yield identifier, bytes_iterator
2222
for fallback in self._fallback_repositories:
2225
desired_files = [(key[0], key[1], identifier)
2226
for identifier, key in viewitems(absent)]
2227
for (identifier, bytes_iterator) in fallback.iter_files_bytes(desired_files):
2228
del absent[identifier]
2229
yield identifier, bytes_iterator
2231
# There may be more missing items, but raise an exception
2233
missing_identifier = next(iter(absent))
2234
missing_key = absent[missing_identifier]
2235
raise errors.RevisionNotPresent(revision_id=missing_key[1],
2236
file_id=missing_key[0])
2237
except errors.UnknownSmartMethod:
2239
for (identifier, bytes_iterator) in (
2240
self._real_repository.iter_files_bytes(desired_files)):
2241
yield identifier, bytes_iterator
2243
def get_cached_parent_map(self, revision_ids):
2244
"""See breezy.CachingParentsProvider.get_cached_parent_map"""
2245
return self._unstacked_provider.get_cached_parent_map(revision_ids)
1397
2247
def get_parent_map(self, revision_ids):
1398
"""See bzrlib.Graph.get_parent_map()."""
2248
"""See breezy.Graph.get_parent_map()."""
1399
2249
return self._make_parents_provider().get_parent_map(revision_ids)
1401
2251
def _get_parent_map_rpc(self, keys):
1532
2371
@needs_read_lock
1533
2372
def get_signature_text(self, revision_id):
1535
return self._real_repository.get_signature_text(revision_id)
2373
path = self.controldir._path_for_remote_call(self._client)
2375
response_tuple, response_handler = self._call_expecting_body(
2376
'Repository.get_revision_signature_text', path, revision_id)
2377
except errors.UnknownSmartMethod:
2379
return self._real_repository.get_signature_text(revision_id)
2380
except errors.NoSuchRevision as err:
2381
for fallback in self._fallback_repositories:
2383
return fallback.get_signature_text(revision_id)
2384
except errors.NoSuchRevision:
2388
if response_tuple[0] != 'ok':
2389
raise errors.UnexpectedSmartServerResponse(response_tuple)
2390
return response_handler.read_body_bytes()
1537
2392
@needs_read_lock
1538
2393
def _get_inventory_xml(self, revision_id):
2394
# This call is used by older working tree formats,
2395
# which stored a serialized basis inventory.
1539
2396
self._ensure_real()
1540
2397
return self._real_repository._get_inventory_xml(revision_id)
1542
2400
def reconcile(self, other=None, thorough=False):
1544
return self._real_repository.reconcile(other=other, thorough=thorough)
2401
from ..reconcile import RepoReconciler
2402
path = self.controldir._path_for_remote_call(self._client)
2404
response, handler = self._call_expecting_body(
2405
'Repository.reconcile', path, self._lock_token)
2406
except (errors.UnknownSmartMethod, errors.TokenLockingNotSupported):
2408
return self._real_repository.reconcile(other=other, thorough=thorough)
2409
if response != ('ok', ):
2410
raise errors.UnexpectedSmartServerResponse(response)
2411
body = handler.read_body_bytes()
2412
result = RepoReconciler(self)
2413
for line in body.split('\n'):
2416
key, val_text = line.split(':')
2417
if key == "garbage_inventories":
2418
result.garbage_inventories = int(val_text)
2419
elif key == "inconsistent_parents":
2420
result.inconsistent_parents = int(val_text)
2422
mutter("unknown reconcile key %r" % key)
1546
2425
def all_revision_ids(self):
1548
return self._real_repository.all_revision_ids()
2426
path = self.controldir._path_for_remote_call(self._client)
2428
response_tuple, response_handler = self._call_expecting_body(
2429
"Repository.all_revision_ids", path)
2430
except errors.UnknownSmartMethod:
2432
return self._real_repository.all_revision_ids()
2433
if response_tuple != ("ok", ):
2434
raise errors.UnexpectedSmartServerResponse(response_tuple)
2435
revids = set(response_handler.read_body_bytes().splitlines())
2436
for fallback in self._fallback_repositories:
2437
revids.update(set(fallback.all_revision_ids()))
2440
def _filtered_revision_trees(self, revision_ids, file_ids):
2441
"""Return Tree for a revision on this branch with only some files.
2443
:param revision_ids: a sequence of revision-ids;
2444
a revision-id may not be None or 'null:'
2445
:param file_ids: if not None, the result is filtered
2446
so that only those file-ids, their parents and their
2447
children are included.
2449
inventories = self.iter_inventories(revision_ids)
2450
for inv in inventories:
2451
# Should we introduce a FilteredRevisionTree class rather
2452
# than pre-filter the inventory here?
2453
filtered_inv = inv.filter(file_ids)
2454
yield InventoryRevisionTree(self, filtered_inv, filtered_inv.revision_id)
1550
2456
@needs_read_lock
1551
2457
def get_deltas_for_revisions(self, revisions, specific_fileids=None):
1553
return self._real_repository.get_deltas_for_revisions(revisions,
1554
specific_fileids=specific_fileids)
2458
medium = self._client._medium
2459
if medium._is_remote_before((1, 2)):
2461
for delta in self._real_repository.get_deltas_for_revisions(
2462
revisions, specific_fileids):
2465
# Get the revision-ids of interest
2466
required_trees = set()
2467
for revision in revisions:
2468
required_trees.add(revision.revision_id)
2469
required_trees.update(revision.parent_ids[:1])
2471
# Get the matching filtered trees. Note that it's more
2472
# efficient to pass filtered trees to changes_from() rather
2473
# than doing the filtering afterwards. changes_from() could
2474
# arguably do the filtering itself but it's path-based, not
2475
# file-id based, so filtering before or afterwards is
2477
if specific_fileids is None:
2478
trees = dict((t.get_revision_id(), t) for
2479
t in self.revision_trees(required_trees))
2481
trees = dict((t.get_revision_id(), t) for
2482
t in self._filtered_revision_trees(required_trees,
2485
# Calculate the deltas
2486
for revision in revisions:
2487
if not revision.parent_ids:
2488
old_tree = self.revision_tree(_mod_revision.NULL_REVISION)
2490
old_tree = trees[revision.parent_ids[0]]
2491
yield trees[revision.revision_id].changes_from(old_tree)
1556
2493
@needs_read_lock
1557
2494
def get_revision_delta(self, revision_id, specific_fileids=None):
1559
return self._real_repository.get_revision_delta(revision_id,
1560
specific_fileids=specific_fileids)
2495
r = self.get_revision(revision_id)
2496
return list(self.get_deltas_for_revisions([r],
2497
specific_fileids=specific_fileids))[0]
1562
2499
@needs_read_lock
1563
2500
def revision_trees(self, revision_ids):
1565
return self._real_repository.revision_trees(revision_ids)
2501
inventories = self.iter_inventories(revision_ids)
2502
for inv in inventories:
2503
yield InventoryRevisionTree(self, inv, inv.revision_id)
1567
2505
@needs_read_lock
1568
2506
def get_revision_reconcile(self, revision_id):
1680
2631
self._ensure_real()
1681
2632
return self._real_repository.texts
2634
def _iter_revisions_rpc(self, revision_ids):
2635
body = "\n".join(revision_ids)
2636
path = self.controldir._path_for_remote_call(self._client)
2637
response_tuple, response_handler = (
2638
self._call_with_body_bytes_expecting_body(
2639
"Repository.iter_revisions", (path, ), body))
2640
if response_tuple[0] != "ok":
2641
raise errors.UnexpectedSmartServerResponse(response_tuple)
2642
serializer_format = response_tuple[1]
2643
serializer = serializer_format_registry.get(serializer_format)
2644
byte_stream = response_handler.read_streamed_body()
2645
decompressor = zlib.decompressobj()
2647
for bytes in byte_stream:
2648
chunks.append(decompressor.decompress(bytes))
2649
if decompressor.unused_data != "":
2650
chunks.append(decompressor.flush())
2651
yield serializer.read_revision_from_string("".join(chunks))
2652
unused = decompressor.unused_data
2653
decompressor = zlib.decompressobj()
2654
chunks = [decompressor.decompress(unused)]
2655
chunks.append(decompressor.flush())
2656
text = "".join(chunks)
2658
yield serializer.read_revision_from_string("".join(chunks))
1683
2660
@needs_read_lock
1684
def get_revisions(self, revision_ids):
1686
return self._real_repository.get_revisions(revision_ids)
2661
def iter_revisions(self, revision_ids):
2662
for rev_id in revision_ids:
2663
if not rev_id or not isinstance(rev_id, bytes):
2664
raise errors.InvalidRevisionId(
2665
revision_id=rev_id, branch=self)
2667
missing = set(revision_ids)
2668
for rev in self._iter_revisions_rpc(revision_ids):
2669
missing.remove(rev.revision_id)
2670
yield (rev.revision_id, rev)
2671
for fallback in self._fallback_repositories:
2674
for (revid, rev) in fallback.iter_revisions(missing):
2677
missing.remove(revid)
2678
for revid in missing:
2680
except errors.UnknownSmartMethod:
2682
for entry in self._real_repository.iter_revisions(revision_ids):
1688
2685
def supports_rich_root(self):
1689
2686
return self._format.rich_root_data
1691
def iter_reverse_revision_history(self, revision_id):
1693
return self._real_repository.iter_reverse_revision_history(revision_id)
1696
2689
def _serializer(self):
1697
2690
return self._format._serializer
1699
2693
def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
1701
return self._real_repository.store_revision_signature(
1702
gpg_strategy, plaintext, revision_id)
2694
signature = gpg_strategy.sign(plaintext)
2695
self.add_signature_text(revision_id, signature)
1704
2697
def add_signature_text(self, revision_id, signature):
1706
return self._real_repository.add_signature_text(revision_id, signature)
2698
if self._real_repository:
2699
# If there is a real repository the write group will
2700
# be in the real repository as well, so use that:
2702
return self._real_repository.add_signature_text(
2703
revision_id, signature)
2704
path = self.controldir._path_for_remote_call(self._client)
2705
response, handler = self._call_with_body_bytes_expecting_body(
2706
'Repository.add_signature_text', (path, self._lock_token,
2707
revision_id) + tuple(self._write_group_tokens), signature)
2708
handler.cancel_read_body()
2710
if response[0] != 'ok':
2711
raise errors.UnexpectedSmartServerResponse(response)
2712
self._write_group_tokens = response[1:]
1708
2714
def has_signature_for_revision_id(self, revision_id):
1710
return self._real_repository.has_signature_for_revision_id(revision_id)
2715
path = self.controldir._path_for_remote_call(self._client)
2717
response = self._call('Repository.has_signature_for_revision_id',
2719
except errors.UnknownSmartMethod:
2721
return self._real_repository.has_signature_for_revision_id(
2723
if response[0] not in ('yes', 'no'):
2724
raise SmartProtocolError('unexpected response code %s' % (response,))
2725
if response[0] == 'yes':
2727
for fallback in self._fallback_repositories:
2728
if fallback.has_signature_for_revision_id(revision_id):
2733
def verify_revision_signature(self, revision_id, gpg_strategy):
2734
if not self.has_signature_for_revision_id(revision_id):
2735
return gpg.SIGNATURE_NOT_SIGNED, None
2736
signature = self.get_signature_text(revision_id)
2738
testament = _mod_testament.Testament.from_revision(self, revision_id)
2739
plaintext = testament.as_short_text()
2741
return gpg_strategy.verify(signature, plaintext)
1712
2743
def item_keys_introduced_by(self, revision_ids, _files_pb=None):
1713
2744
self._ensure_real()
1714
2745
return self._real_repository.item_keys_introduced_by(revision_ids,
1715
2746
_files_pb=_files_pb)
1717
def revision_graph_can_have_wrong_parents(self):
1718
# The answer depends on the remote repo format.
1720
return self._real_repository.revision_graph_can_have_wrong_parents()
1722
2748
def _find_inconsistent_revision_parents(self, revisions_iterator=None):
1723
2749
self._ensure_real()
1724
2750
return self._real_repository._find_inconsistent_revision_parents(
2059
3096
def network_name(self):
2060
3097
return self._network_name
2062
def open(self, a_bzrdir, name=None, ignore_fallbacks=False):
2063
return a_bzrdir.open_branch(name=name,
3099
def open(self, a_controldir, name=None, ignore_fallbacks=False):
3100
return a_controldir.open_branch(name=name,
2064
3101
ignore_fallbacks=ignore_fallbacks)
2066
def _vfs_initialize(self, a_bzrdir, name):
3103
def _vfs_initialize(self, a_controldir, name, append_revisions_only,
2067
3105
# Initialisation when using a local bzrdir object, or a non-vfs init
2068
3106
# method is not available on the server.
2069
3107
# self._custom_format is always set - the start of initialize ensures
2071
if isinstance(a_bzrdir, RemoteBzrDir):
2072
a_bzrdir._ensure_real()
2073
result = self._custom_format.initialize(a_bzrdir._real_bzrdir,
3109
if isinstance(a_controldir, RemoteBzrDir):
3110
a_controldir._ensure_real()
3111
result = self._custom_format.initialize(a_controldir._real_bzrdir,
3112
name=name, append_revisions_only=append_revisions_only,
3113
repository=repository)
2076
3115
# We assume the bzrdir is parameterised; it may not be.
2077
result = self._custom_format.initialize(a_bzrdir, name)
2078
if (isinstance(a_bzrdir, RemoteBzrDir) and
3116
result = self._custom_format.initialize(a_controldir, name=name,
3117
append_revisions_only=append_revisions_only,
3118
repository=repository)
3119
if (isinstance(a_controldir, RemoteBzrDir) and
2079
3120
not isinstance(result, RemoteBranch)):
2080
result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result,
3121
result = RemoteBranch(a_controldir, a_controldir.find_repository(), result,
2084
def initialize(self, a_bzrdir, name=None):
3125
def initialize(self, a_controldir, name=None, repository=None,
3126
append_revisions_only=None):
3128
name = a_controldir._get_selected_branch()
2085
3129
# 1) get the network name to use.
2086
3130
if self._custom_format:
2087
3131
network_name = self._custom_format.network_name()
2089
# Select the current bzrlib default and ask for that.
2090
reference_bzrdir_format = bzrdir.format_registry.get('default')()
3133
# Select the current breezy default and ask for that.
3134
reference_bzrdir_format = controldir.format_registry.get('default')()
2091
3135
reference_format = reference_bzrdir_format.get_branch_format()
2092
3136
self._custom_format = reference_format
2093
3137
network_name = reference_format.network_name()
2094
3138
# Being asked to create on a non RemoteBzrDir:
2095
if not isinstance(a_bzrdir, RemoteBzrDir):
2096
return self._vfs_initialize(a_bzrdir, name=name)
2097
medium = a_bzrdir._client._medium
3139
if not isinstance(a_controldir, RemoteBzrDir):
3140
return self._vfs_initialize(a_controldir, name=name,
3141
append_revisions_only=append_revisions_only,
3142
repository=repository)
3143
medium = a_controldir._client._medium
2098
3144
if medium._is_remote_before((1, 13)):
2099
return self._vfs_initialize(a_bzrdir, name=name)
3145
return self._vfs_initialize(a_controldir, name=name,
3146
append_revisions_only=append_revisions_only,
3147
repository=repository)
2100
3148
# Creating on a remote bzr dir.
2101
3149
# 2) try direct creation via RPC
2102
path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
2103
if name is not None:
3150
path = a_controldir._path_for_remote_call(a_controldir._client)
2104
3152
# XXX JRV20100304: Support creating colocated branches
2105
3153
raise errors.NoColocatedBranchSupport(self)
2106
3154
verb = 'BzrDir.create_branch'
2108
response = a_bzrdir._call(verb, path, network_name)
3156
response = a_controldir._call(verb, path, network_name)
2109
3157
except errors.UnknownSmartMethod:
2110
3158
# Fallback - use vfs methods
2111
3159
medium._remember_remote_is_before((1, 13))
2112
return self._vfs_initialize(a_bzrdir, name=name)
3160
return self._vfs_initialize(a_controldir, name=name,
3161
append_revisions_only=append_revisions_only,
3162
repository=repository)
2113
3163
if response[0] != 'ok':
2114
3164
raise errors.UnexpectedSmartServerResponse(response)
2115
3165
# Turn the response into a RemoteRepository object.
2116
3166
format = RemoteBranchFormat(network_name=response[1])
2117
3167
repo_format = response_tuple_to_repo_format(response[3:])
2118
if response[2] == '':
2119
repo_bzrdir = a_bzrdir
3168
repo_path = response[2]
3169
if repository is not None:
3170
remote_repo_url = urlutils.join(a_controldir.user_url, repo_path)
3171
url_diff = urlutils.relative_url(repository.user_url,
3174
raise AssertionError(
3175
'repository.user_url %r does not match URL from server '
3176
'response (%r + %r)'
3177
% (repository.user_url, a_controldir.user_url, repo_path))
3178
remote_repo = repository
2121
repo_bzrdir = RemoteBzrDir(
2122
a_bzrdir.root_transport.clone(response[2]), a_bzrdir._format,
2124
remote_repo = RemoteRepository(repo_bzrdir, repo_format)
2125
remote_branch = RemoteBranch(a_bzrdir, remote_repo,
3181
repo_bzrdir = a_controldir
3183
repo_bzrdir = RemoteBzrDir(
3184
a_controldir.root_transport.clone(repo_path), a_controldir._format,
3185
a_controldir._client)
3186
remote_repo = RemoteRepository(repo_bzrdir, repo_format)
3187
remote_branch = RemoteBranch(a_controldir, remote_repo,
2126
3188
format=format, setup_stacking=False, name=name)
3189
if append_revisions_only:
3190
remote_branch.set_append_revisions_only(append_revisions_only)
2127
3191
# XXX: We know this is a new branch, so it must have revno 0, revid
2128
3192
# NULL_REVISION. Creating the branch locked would make this be unable
2129
3193
# to be wrong; here its simply very unlikely to be wrong. RBC 20090225
2148
3212
self._ensure_real()
2149
3213
return self._custom_format.supports_set_append_revisions_only()
3215
def _use_default_local_heads_to_fetch(self):
3216
# If the branch format is a metadir format *and* its heads_to_fetch
3217
# implementation is not overridden vs the base class, we can use the
3218
# base class logic rather than use the heads_to_fetch RPC. This is
3219
# usually cheaper in terms of net round trips, as the last-revision and
3220
# tags info fetched is cached and would be fetched anyway.
3222
if isinstance(self._custom_format, bzrbranch.BranchFormatMetadir):
3223
branch_class = self._custom_format._branch_class()
3224
heads_to_fetch_impl = branch_class.heads_to_fetch.__func__
3225
if heads_to_fetch_impl is branch.Branch.heads_to_fetch.__func__:
3230
class RemoteBranchStore(_mod_config.IniFileStore):
3231
"""Branch store which attempts to use HPSS calls to retrieve branch store.
3233
Note that this is specific to bzr-based formats.
3236
def __init__(self, branch):
3237
super(RemoteBranchStore, self).__init__()
3238
self.branch = branch
3240
self._real_store = None
3242
def external_url(self):
3243
return urlutils.join(self.branch.user_url, 'branch.conf')
3245
def _load_content(self):
3246
path = self.branch._remote_path()
3248
response, handler = self.branch._call_expecting_body(
3249
'Branch.get_config_file', path)
3250
except errors.UnknownSmartMethod:
3252
return self._real_store._load_content()
3253
if len(response) and response[0] != 'ok':
3254
raise errors.UnexpectedSmartServerResponse(response)
3255
return handler.read_body_bytes()
3257
def _save_content(self, content):
3258
path = self.branch._remote_path()
3260
response, handler = self.branch._call_with_body_bytes_expecting_body(
3261
'Branch.put_config_file', (path,
3262
self.branch._lock_token, self.branch._repo_lock_token),
3264
except errors.UnknownSmartMethod:
3266
return self._real_store._save_content(content)
3267
handler.cancel_read_body()
3268
if response != ('ok', ):
3269
raise errors.UnexpectedSmartServerResponse(response)
3271
def _ensure_real(self):
3272
self.branch._ensure_real()
3273
if self._real_store is None:
3274
self._real_store = _mod_config.BranchStore(self.branch)
2152
3277
class RemoteBranch(branch.Branch, _RpcHelper, lock._RelockDebugMixin):
2153
3278
"""Branch stored on a server accessed by HPSS RPC.
2654
3828
_override_hook_target=self, **kwargs)
2656
3830
@needs_read_lock
2657
def push(self, target, overwrite=False, stop_revision=None):
3831
def push(self, target, overwrite=False, stop_revision=None, lossy=False):
2658
3832
self._ensure_real()
2659
3833
return self._real_branch.push(
2660
target, overwrite=overwrite, stop_revision=stop_revision,
3834
target, overwrite=overwrite, stop_revision=stop_revision, lossy=lossy,
2661
3835
_override_hook_source_branch=self)
3837
def peek_lock_mode(self):
3838
return self._lock_mode
2663
3840
def is_locked(self):
2664
3841
return self._lock_count >= 1
2666
3843
@needs_read_lock
3844
def revision_id_to_dotted_revno(self, revision_id):
3845
"""Given a revision id, return its dotted revno.
3847
:return: a tuple like (1,) or (400,1,3).
3850
response = self._call('Branch.revision_id_to_revno',
3851
self._remote_path(), revision_id)
3852
except errors.UnknownSmartMethod:
3854
return self._real_branch.revision_id_to_dotted_revno(revision_id)
3855
if response[0] == 'ok':
3856
return tuple([int(x) for x in response[1:]])
3858
raise errors.UnexpectedSmartServerResponse(response)
2667
3861
def revision_id_to_revno(self, revision_id):
2669
return self._real_branch.revision_id_to_revno(revision_id)
3862
"""Given a revision id on the branch mainline, return its revno.
3867
response = self._call('Branch.revision_id_to_revno',
3868
self._remote_path(), revision_id)
3869
except errors.UnknownSmartMethod:
3871
return self._real_branch.revision_id_to_revno(revision_id)
3872
if response[0] == 'ok':
3873
if len(response) == 2:
3874
return int(response[1])
3875
raise NoSuchRevision(self, revision_id)
3877
raise errors.UnexpectedSmartServerResponse(response)
2671
3879
@needs_write_lock
2672
3880
def set_last_revision_info(self, revno, revision_id):
2673
3881
# XXX: These should be returned by the set_last_revision_info verb
2674
3882
old_revno, old_revid = self.last_revision_info()
2675
3883
self._run_pre_change_branch_tip_hooks(revno, revision_id)
2676
revision_id = ensure_null(revision_id)
3884
if not revision_id or not isinstance(revision_id, bytes):
3885
raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2678
3887
response = self._call('Branch.set_last_revision_info',
2679
3888
self._remote_path(), self._lock_token, self._repo_lock_token,
2708
3917
except errors.UnknownSmartMethod:
2709
3918
medium._remember_remote_is_before((1, 6))
2710
3919
self._clear_cached_state_of_remote_branch_only()
2711
self.set_revision_history(self._lefthand_history(revision_id,
2712
last_rev=last_rev,other_branch=other_branch))
3920
graph = self.repository.get_graph()
3921
(last_revno, last_revid) = self.last_revision_info()
3922
known_revision_ids = [
3923
(last_revid, last_revno),
3924
(_mod_revision.NULL_REVISION, 0),
3926
if last_rev is not None:
3927
if not graph.is_ancestor(last_rev, revision_id):
3928
# our previous tip is not merged into stop_revision
3929
raise errors.DivergedBranches(self, other_branch)
3930
revno = graph.find_distance_to_null(revision_id, known_revision_ids)
3931
self.set_last_revision_info(revno, revision_id)
2714
3933
def set_push_location(self, location):
3934
self._set_config_location('push_location', location)
3936
def heads_to_fetch(self):
3937
if self._format._use_default_local_heads_to_fetch():
3938
# We recognise this format, and its heads-to-fetch implementation
3939
# is the default one (tip + tags). In this case it's cheaper to
3940
# just use the default implementation rather than a special RPC as
3941
# the tip and tags data is cached.
3942
return branch.Branch.heads_to_fetch(self)
3943
medium = self._client._medium
3944
if medium._is_remote_before((2, 4)):
3945
return self._vfs_heads_to_fetch()
3947
return self._rpc_heads_to_fetch()
3948
except errors.UnknownSmartMethod:
3949
medium._remember_remote_is_before((2, 4))
3950
return self._vfs_heads_to_fetch()
3952
def _rpc_heads_to_fetch(self):
3953
response = self._call('Branch.heads_to_fetch', self._remote_path())
3954
if len(response) != 2:
3955
raise errors.UnexpectedSmartServerResponse(response)
3956
must_fetch, if_present_fetch = response
3957
return set(must_fetch), set(if_present_fetch)
3959
def _vfs_heads_to_fetch(self):
2715
3960
self._ensure_real()
2716
return self._real_branch.set_push_location(location)
3961
return self._real_branch.heads_to_fetch()
2719
3964
class RemoteConfig(object):
2774
4029
medium = self._branch._client._medium
2775
4030
if medium._is_remote_before((1, 14)):
2776
4031
return self._vfs_set_option(value, name, section)
4032
if isinstance(value, dict):
4033
if medium._is_remote_before((2, 2)):
4034
return self._vfs_set_option(value, name, section)
4035
return self._set_config_option_dict(value, name, section)
4037
return self._set_config_option(value, name, section)
4039
def _set_config_option(self, value, name, section):
2778
4041
path = self._branch._remote_path()
2779
4042
response = self._branch._client.call('Branch.set_config_option',
2780
4043
path, self._branch._lock_token, self._branch._repo_lock_token,
2781
4044
value.encode('utf8'), name, section or '')
2782
4045
except errors.UnknownSmartMethod:
4046
medium = self._branch._client._medium
2783
4047
medium._remember_remote_is_before((1, 14))
2784
4048
return self._vfs_set_option(value, name, section)
2785
4049
if response != ():
2786
4050
raise errors.UnexpectedSmartServerResponse(response)
4052
def _serialize_option_dict(self, option_dict):
4054
for key, value in option_dict.items():
4055
if isinstance(key, unicode):
4056
key = key.encode('utf8')
4057
if isinstance(value, unicode):
4058
value = value.encode('utf8')
4059
utf8_dict[key] = value
4060
return bencode.bencode(utf8_dict)
4062
def _set_config_option_dict(self, value, name, section):
4064
path = self._branch._remote_path()
4065
serialised_dict = self._serialize_option_dict(value)
4066
response = self._branch._client.call(
4067
'Branch.set_config_option_dict',
4068
path, self._branch._lock_token, self._branch._repo_lock_token,
4069
serialised_dict, name, section or '')
4070
except errors.UnknownSmartMethod:
4071
medium = self._branch._client._medium
4072
medium._remember_remote_is_before((2, 2))
4073
return self._vfs_set_option(value, name, section)
4075
raise errors.UnexpectedSmartServerResponse(response)
2788
4077
def _real_object(self):
2789
4078
self._branch._ensure_real()
2790
4079
return self._branch._real_branch
2867
4150
return context['path']
2868
except KeyError, key_err:
4151
except KeyError as key_err:
2870
4153
return err.error_args[0]
2871
except IndexError, idx_err:
4154
except IndexError as idx_err:
2873
4156
'Missing key %r in context %r', key_err.args[0], context)
2876
if err.error_verb == 'IncompatibleRepositories':
2877
raise errors.IncompatibleRepositories(err.error_args[0],
2878
err.error_args[1], err.error_args[2])
2879
elif err.error_verb == 'NoSuchRevision':
2880
raise NoSuchRevision(find('branch'), err.error_args[0])
2881
elif err.error_verb == 'nosuchrevision':
2882
raise NoSuchRevision(find('repository'), err.error_args[0])
2883
elif err.error_verb == 'nobranch':
2884
if len(err.error_args) >= 1:
2885
extra = err.error_args[0]
2888
raise errors.NotBranchError(path=find('bzrdir').root_transport.base,
2890
elif err.error_verb == 'norepository':
2891
raise errors.NoRepositoryPresent(find('bzrdir'))
2892
elif err.error_verb == 'LockContention':
2893
raise errors.LockContention('(remote lock)')
2894
elif err.error_verb == 'UnlockableTransport':
2895
raise errors.UnlockableTransport(find('bzrdir').root_transport)
2896
elif err.error_verb == 'LockFailed':
2897
raise errors.LockFailed(err.error_args[0], err.error_args[1])
2898
elif err.error_verb == 'TokenMismatch':
2899
raise errors.TokenMismatch(find('token'), '(remote token)')
2900
elif err.error_verb == 'Diverged':
2901
raise errors.DivergedBranches(find('branch'), find('other_branch'))
2902
elif err.error_verb == 'TipChangeRejected':
2903
raise errors.TipChangeRejected(err.error_args[0].decode('utf8'))
2904
elif err.error_verb == 'UnstackableBranchFormat':
2905
raise errors.UnstackableBranchFormat(*err.error_args)
2906
elif err.error_verb == 'UnstackableRepositoryFormat':
2907
raise errors.UnstackableRepositoryFormat(*err.error_args)
2908
elif err.error_verb == 'NotStacked':
2909
raise errors.NotStacked(branch=find('branch'))
2910
elif err.error_verb == 'PermissionDenied':
2912
if len(err.error_args) >= 2:
2913
extra = err.error_args[1]
2916
raise errors.PermissionDenied(path, extra=extra)
2917
elif err.error_verb == 'ReadError':
2919
raise errors.ReadError(path)
2920
elif err.error_verb == 'NoSuchFile':
2922
raise errors.NoSuchFile(path)
2923
elif err.error_verb == 'FileExists':
2924
raise errors.FileExists(err.error_args[0])
2925
elif err.error_verb == 'DirectoryNotEmpty':
2926
raise errors.DirectoryNotEmpty(err.error_args[0])
2927
elif err.error_verb == 'ShortReadvError':
2928
args = err.error_args
2929
raise errors.ShortReadvError(
2930
args[0], int(args[1]), int(args[2]), int(args[3]))
2931
elif err.error_verb in ('UnicodeEncodeError', 'UnicodeDecodeError'):
4160
translator = error_translators.get(err.error_verb)
4164
raise translator(err, find, get_path)
4166
translator = no_context_error_translators.get(err.error_verb)
4168
raise errors.UnknownErrorFromSmartServer(err)
4170
raise translator(err)
4173
error_translators.register('NoSuchRevision',
4174
lambda err, find, get_path: NoSuchRevision(
4175
find('branch'), err.error_args[0]))
4176
error_translators.register('nosuchrevision',
4177
lambda err, find, get_path: NoSuchRevision(
4178
find('repository'), err.error_args[0]))
4180
def _translate_nobranch_error(err, find, get_path):
4181
if len(err.error_args) >= 1:
4182
extra = err.error_args[0]
4185
return errors.NotBranchError(path=find('bzrdir').root_transport.base,
4188
error_translators.register('nobranch', _translate_nobranch_error)
4189
error_translators.register('norepository',
4190
lambda err, find, get_path: errors.NoRepositoryPresent(
4192
error_translators.register('UnlockableTransport',
4193
lambda err, find, get_path: errors.UnlockableTransport(
4194
find('bzrdir').root_transport))
4195
error_translators.register('TokenMismatch',
4196
lambda err, find, get_path: errors.TokenMismatch(
4197
find('token'), '(remote token)'))
4198
error_translators.register('Diverged',
4199
lambda err, find, get_path: errors.DivergedBranches(
4200
find('branch'), find('other_branch')))
4201
error_translators.register('NotStacked',
4202
lambda err, find, get_path: errors.NotStacked(branch=find('branch')))
4204
def _translate_PermissionDenied(err, find, get_path):
4206
if len(err.error_args) >= 2:
4207
extra = err.error_args[1]
4210
return errors.PermissionDenied(path, extra=extra)
4212
error_translators.register('PermissionDenied', _translate_PermissionDenied)
4213
error_translators.register('ReadError',
4214
lambda err, find, get_path: errors.ReadError(get_path()))
4215
error_translators.register('NoSuchFile',
4216
lambda err, find, get_path: errors.NoSuchFile(get_path()))
4217
error_translators.register('TokenLockingNotSupported',
4218
lambda err, find, get_path: errors.TokenLockingNotSupported(
4219
find('repository')))
4220
error_translators.register('UnsuspendableWriteGroup',
4221
lambda err, find, get_path: errors.UnsuspendableWriteGroup(
4222
repository=find('repository')))
4223
error_translators.register('UnresumableWriteGroup',
4224
lambda err, find, get_path: errors.UnresumableWriteGroup(
4225
repository=find('repository'), write_groups=err.error_args[0],
4226
reason=err.error_args[1]))
4227
no_context_error_translators.register('IncompatibleRepositories',
4228
lambda err: errors.IncompatibleRepositories(
4229
err.error_args[0], err.error_args[1], err.error_args[2]))
4230
no_context_error_translators.register('LockContention',
4231
lambda err: errors.LockContention('(remote lock)'))
4232
no_context_error_translators.register('LockFailed',
4233
lambda err: errors.LockFailed(err.error_args[0], err.error_args[1]))
4234
no_context_error_translators.register('TipChangeRejected',
4235
lambda err: errors.TipChangeRejected(err.error_args[0].decode('utf8')))
4236
no_context_error_translators.register('UnstackableBranchFormat',
4237
lambda err: branch.UnstackableBranchFormat(*err.error_args))
4238
no_context_error_translators.register('UnstackableRepositoryFormat',
4239
lambda err: errors.UnstackableRepositoryFormat(*err.error_args))
4240
no_context_error_translators.register('FileExists',
4241
lambda err: errors.FileExists(err.error_args[0]))
4242
no_context_error_translators.register('DirectoryNotEmpty',
4243
lambda err: errors.DirectoryNotEmpty(err.error_args[0]))
4245
def _translate_short_readv_error(err):
4246
args = err.error_args
4247
return errors.ShortReadvError(args[0], int(args[1]), int(args[2]),
4250
no_context_error_translators.register('ShortReadvError',
4251
_translate_short_readv_error)
4253
def _translate_unicode_error(err):
2932
4254
encoding = str(err.error_args[0]) # encoding must always be a string
2933
4255
val = err.error_args[1]
2934
4256
start = int(err.error_args[2])