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
2661
def get_revisions(self, revision_ids):
1686
return self._real_repository.get_revisions(revision_ids)
2662
if revision_ids is None:
2663
revision_ids = self.all_revision_ids()
2665
for rev_id in revision_ids:
2666
if not rev_id or not isinstance(rev_id, bytes):
2667
raise errors.InvalidRevisionId(
2668
revision_id=rev_id, branch=self)
2670
missing = set(revision_ids)
2672
for rev in self._iter_revisions_rpc(revision_ids):
2673
missing.remove(rev.revision_id)
2674
revs[rev.revision_id] = rev
2675
except errors.UnknownSmartMethod:
2677
return self._real_repository.get_revisions(revision_ids)
2678
for fallback in self._fallback_repositories:
2681
for revid in list(missing):
2682
# XXX JRV 2011-11-20: It would be nice if there was a
2683
# public method on Repository that could be used to query
2684
# for revision objects *without* failing completely if one
2685
# was missing. There is VersionedFileRepository._iter_revisions,
2686
# but unfortunately that's private and not provided by
2687
# all repository implementations.
2689
revs[revid] = fallback.get_revision(revid)
2690
except errors.NoSuchRevision:
2693
missing.remove(revid)
2695
raise errors.NoSuchRevision(self, list(missing)[0])
2696
return [revs[revid] for revid in revision_ids]
1688
2698
def supports_rich_root(self):
1689
2699
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
2702
def _serializer(self):
1697
2703
return self._format._serializer
1699
2706
def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
1701
return self._real_repository.store_revision_signature(
1702
gpg_strategy, plaintext, revision_id)
2707
signature = gpg_strategy.sign(plaintext)
2708
self.add_signature_text(revision_id, signature)
1704
2710
def add_signature_text(self, revision_id, signature):
1706
return self._real_repository.add_signature_text(revision_id, signature)
2711
if self._real_repository:
2712
# If there is a real repository the write group will
2713
# be in the real repository as well, so use that:
2715
return self._real_repository.add_signature_text(
2716
revision_id, signature)
2717
path = self.controldir._path_for_remote_call(self._client)
2718
response, handler = self._call_with_body_bytes_expecting_body(
2719
'Repository.add_signature_text', (path, self._lock_token,
2720
revision_id) + tuple(self._write_group_tokens), signature)
2721
handler.cancel_read_body()
2723
if response[0] != 'ok':
2724
raise errors.UnexpectedSmartServerResponse(response)
2725
self._write_group_tokens = response[1:]
1708
2727
def has_signature_for_revision_id(self, revision_id):
1710
return self._real_repository.has_signature_for_revision_id(revision_id)
2728
path = self.controldir._path_for_remote_call(self._client)
2730
response = self._call('Repository.has_signature_for_revision_id',
2732
except errors.UnknownSmartMethod:
2734
return self._real_repository.has_signature_for_revision_id(
2736
if response[0] not in ('yes', 'no'):
2737
raise SmartProtocolError('unexpected response code %s' % (response,))
2738
if response[0] == 'yes':
2740
for fallback in self._fallback_repositories:
2741
if fallback.has_signature_for_revision_id(revision_id):
2746
def verify_revision_signature(self, revision_id, gpg_strategy):
2747
if not self.has_signature_for_revision_id(revision_id):
2748
return gpg.SIGNATURE_NOT_SIGNED, None
2749
signature = self.get_signature_text(revision_id)
2751
testament = _mod_testament.Testament.from_revision(self, revision_id)
2752
plaintext = testament.as_short_text()
2754
return gpg_strategy.verify(signature, plaintext)
1712
2756
def item_keys_introduced_by(self, revision_ids, _files_pb=None):
1713
2757
self._ensure_real()
1714
2758
return self._real_repository.item_keys_introduced_by(revision_ids,
1715
2759
_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
2761
def _find_inconsistent_revision_parents(self, revisions_iterator=None):
1723
2762
self._ensure_real()
1724
2763
return self._real_repository._find_inconsistent_revision_parents(
2059
3109
def network_name(self):
2060
3110
return self._network_name
2062
def open(self, a_bzrdir, name=None, ignore_fallbacks=False):
2063
return a_bzrdir.open_branch(name=name,
3112
def open(self, a_controldir, name=None, ignore_fallbacks=False):
3113
return a_controldir.open_branch(name=name,
2064
3114
ignore_fallbacks=ignore_fallbacks)
2066
def _vfs_initialize(self, a_bzrdir, name):
3116
def _vfs_initialize(self, a_controldir, name, append_revisions_only,
2067
3118
# Initialisation when using a local bzrdir object, or a non-vfs init
2068
3119
# method is not available on the server.
2069
3120
# 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,
3122
if isinstance(a_controldir, RemoteBzrDir):
3123
a_controldir._ensure_real()
3124
result = self._custom_format.initialize(a_controldir._real_bzrdir,
3125
name=name, append_revisions_only=append_revisions_only,
3126
repository=repository)
2076
3128
# 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
3129
result = self._custom_format.initialize(a_controldir, name=name,
3130
append_revisions_only=append_revisions_only,
3131
repository=repository)
3132
if (isinstance(a_controldir, RemoteBzrDir) and
2079
3133
not isinstance(result, RemoteBranch)):
2080
result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result,
3134
result = RemoteBranch(a_controldir, a_controldir.find_repository(), result,
2084
def initialize(self, a_bzrdir, name=None):
3138
def initialize(self, a_controldir, name=None, repository=None,
3139
append_revisions_only=None):
3141
name = a_controldir._get_selected_branch()
2085
3142
# 1) get the network name to use.
2086
3143
if self._custom_format:
2087
3144
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')()
3146
# Select the current breezy default and ask for that.
3147
reference_bzrdir_format = controldir.format_registry.get('default')()
2091
3148
reference_format = reference_bzrdir_format.get_branch_format()
2092
3149
self._custom_format = reference_format
2093
3150
network_name = reference_format.network_name()
2094
3151
# 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
3152
if not isinstance(a_controldir, RemoteBzrDir):
3153
return self._vfs_initialize(a_controldir, name=name,
3154
append_revisions_only=append_revisions_only,
3155
repository=repository)
3156
medium = a_controldir._client._medium
2098
3157
if medium._is_remote_before((1, 13)):
2099
return self._vfs_initialize(a_bzrdir, name=name)
3158
return self._vfs_initialize(a_controldir, name=name,
3159
append_revisions_only=append_revisions_only,
3160
repository=repository)
2100
3161
# Creating on a remote bzr dir.
2101
3162
# 2) try direct creation via RPC
2102
path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
2103
if name is not None:
3163
path = a_controldir._path_for_remote_call(a_controldir._client)
2104
3165
# XXX JRV20100304: Support creating colocated branches
2105
3166
raise errors.NoColocatedBranchSupport(self)
2106
3167
verb = 'BzrDir.create_branch'
2108
response = a_bzrdir._call(verb, path, network_name)
3169
response = a_controldir._call(verb, path, network_name)
2109
3170
except errors.UnknownSmartMethod:
2110
3171
# Fallback - use vfs methods
2111
3172
medium._remember_remote_is_before((1, 13))
2112
return self._vfs_initialize(a_bzrdir, name=name)
3173
return self._vfs_initialize(a_controldir, name=name,
3174
append_revisions_only=append_revisions_only,
3175
repository=repository)
2113
3176
if response[0] != 'ok':
2114
3177
raise errors.UnexpectedSmartServerResponse(response)
2115
3178
# Turn the response into a RemoteRepository object.
2116
3179
format = RemoteBranchFormat(network_name=response[1])
2117
3180
repo_format = response_tuple_to_repo_format(response[3:])
2118
if response[2] == '':
2119
repo_bzrdir = a_bzrdir
3181
repo_path = response[2]
3182
if repository is not None:
3183
remote_repo_url = urlutils.join(a_controldir.user_url, repo_path)
3184
url_diff = urlutils.relative_url(repository.user_url,
3187
raise AssertionError(
3188
'repository.user_url %r does not match URL from server '
3189
'response (%r + %r)'
3190
% (repository.user_url, a_controldir.user_url, repo_path))
3191
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,
3194
repo_bzrdir = a_controldir
3196
repo_bzrdir = RemoteBzrDir(
3197
a_controldir.root_transport.clone(repo_path), a_controldir._format,
3198
a_controldir._client)
3199
remote_repo = RemoteRepository(repo_bzrdir, repo_format)
3200
remote_branch = RemoteBranch(a_controldir, remote_repo,
2126
3201
format=format, setup_stacking=False, name=name)
3202
if append_revisions_only:
3203
remote_branch.set_append_revisions_only(append_revisions_only)
2127
3204
# XXX: We know this is a new branch, so it must have revno 0, revid
2128
3205
# NULL_REVISION. Creating the branch locked would make this be unable
2129
3206
# to be wrong; here its simply very unlikely to be wrong. RBC 20090225
2148
3225
self._ensure_real()
2149
3226
return self._custom_format.supports_set_append_revisions_only()
3228
def _use_default_local_heads_to_fetch(self):
3229
# If the branch format is a metadir format *and* its heads_to_fetch
3230
# implementation is not overridden vs the base class, we can use the
3231
# base class logic rather than use the heads_to_fetch RPC. This is
3232
# usually cheaper in terms of net round trips, as the last-revision and
3233
# tags info fetched is cached and would be fetched anyway.
3235
if isinstance(self._custom_format, bzrbranch.BranchFormatMetadir):
3236
branch_class = self._custom_format._branch_class()
3237
heads_to_fetch_impl = branch_class.heads_to_fetch.__func__
3238
if heads_to_fetch_impl is branch.Branch.heads_to_fetch.__func__:
3243
class RemoteBranchStore(_mod_config.IniFileStore):
3244
"""Branch store which attempts to use HPSS calls to retrieve branch store.
3246
Note that this is specific to bzr-based formats.
3249
def __init__(self, branch):
3250
super(RemoteBranchStore, self).__init__()
3251
self.branch = branch
3253
self._real_store = None
3255
def external_url(self):
3256
return urlutils.join(self.branch.user_url, 'branch.conf')
3258
def _load_content(self):
3259
path = self.branch._remote_path()
3261
response, handler = self.branch._call_expecting_body(
3262
'Branch.get_config_file', path)
3263
except errors.UnknownSmartMethod:
3265
return self._real_store._load_content()
3266
if len(response) and response[0] != 'ok':
3267
raise errors.UnexpectedSmartServerResponse(response)
3268
return handler.read_body_bytes()
3270
def _save_content(self, content):
3271
path = self.branch._remote_path()
3273
response, handler = self.branch._call_with_body_bytes_expecting_body(
3274
'Branch.put_config_file', (path,
3275
self.branch._lock_token, self.branch._repo_lock_token),
3277
except errors.UnknownSmartMethod:
3279
return self._real_store._save_content(content)
3280
handler.cancel_read_body()
3281
if response != ('ok', ):
3282
raise errors.UnexpectedSmartServerResponse(response)
3284
def _ensure_real(self):
3285
self.branch._ensure_real()
3286
if self._real_store is None:
3287
self._real_store = _mod_config.BranchStore(self.branch)
2152
3290
class RemoteBranch(branch.Branch, _RpcHelper, lock._RelockDebugMixin):
2153
3291
"""Branch stored on a server accessed by HPSS RPC.
2654
3841
_override_hook_target=self, **kwargs)
2656
3843
@needs_read_lock
2657
def push(self, target, overwrite=False, stop_revision=None):
3844
def push(self, target, overwrite=False, stop_revision=None, lossy=False):
2658
3845
self._ensure_real()
2659
3846
return self._real_branch.push(
2660
target, overwrite=overwrite, stop_revision=stop_revision,
3847
target, overwrite=overwrite, stop_revision=stop_revision, lossy=lossy,
2661
3848
_override_hook_source_branch=self)
3850
def peek_lock_mode(self):
3851
return self._lock_mode
2663
3853
def is_locked(self):
2664
3854
return self._lock_count >= 1
2666
3856
@needs_read_lock
3857
def revision_id_to_dotted_revno(self, revision_id):
3858
"""Given a revision id, return its dotted revno.
3860
:return: a tuple like (1,) or (400,1,3).
3863
response = self._call('Branch.revision_id_to_revno',
3864
self._remote_path(), revision_id)
3865
except errors.UnknownSmartMethod:
3867
return self._real_branch.revision_id_to_dotted_revno(revision_id)
3868
if response[0] == 'ok':
3869
return tuple([int(x) for x in response[1:]])
3871
raise errors.UnexpectedSmartServerResponse(response)
2667
3874
def revision_id_to_revno(self, revision_id):
2669
return self._real_branch.revision_id_to_revno(revision_id)
3875
"""Given a revision id on the branch mainline, return its revno.
3880
response = self._call('Branch.revision_id_to_revno',
3881
self._remote_path(), revision_id)
3882
except errors.UnknownSmartMethod:
3884
return self._real_branch.revision_id_to_revno(revision_id)
3885
if response[0] == 'ok':
3886
if len(response) == 2:
3887
return int(response[1])
3888
raise NoSuchRevision(self, revision_id)
3890
raise errors.UnexpectedSmartServerResponse(response)
2671
3892
@needs_write_lock
2672
3893
def set_last_revision_info(self, revno, revision_id):
2673
3894
# XXX: These should be returned by the set_last_revision_info verb
2674
3895
old_revno, old_revid = self.last_revision_info()
2675
3896
self._run_pre_change_branch_tip_hooks(revno, revision_id)
2676
revision_id = ensure_null(revision_id)
3897
if not revision_id or not isinstance(revision_id, bytes):
3898
raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2678
3900
response = self._call('Branch.set_last_revision_info',
2679
3901
self._remote_path(), self._lock_token, self._repo_lock_token,
2708
3930
except errors.UnknownSmartMethod:
2709
3931
medium._remember_remote_is_before((1, 6))
2710
3932
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))
3933
graph = self.repository.get_graph()
3934
(last_revno, last_revid) = self.last_revision_info()
3935
known_revision_ids = [
3936
(last_revid, last_revno),
3937
(_mod_revision.NULL_REVISION, 0),
3939
if last_rev is not None:
3940
if not graph.is_ancestor(last_rev, revision_id):
3941
# our previous tip is not merged into stop_revision
3942
raise errors.DivergedBranches(self, other_branch)
3943
revno = graph.find_distance_to_null(revision_id, known_revision_ids)
3944
self.set_last_revision_info(revno, revision_id)
2714
3946
def set_push_location(self, location):
3947
self._set_config_location('push_location', location)
3949
def heads_to_fetch(self):
3950
if self._format._use_default_local_heads_to_fetch():
3951
# We recognise this format, and its heads-to-fetch implementation
3952
# is the default one (tip + tags). In this case it's cheaper to
3953
# just use the default implementation rather than a special RPC as
3954
# the tip and tags data is cached.
3955
return branch.Branch.heads_to_fetch(self)
3956
medium = self._client._medium
3957
if medium._is_remote_before((2, 4)):
3958
return self._vfs_heads_to_fetch()
3960
return self._rpc_heads_to_fetch()
3961
except errors.UnknownSmartMethod:
3962
medium._remember_remote_is_before((2, 4))
3963
return self._vfs_heads_to_fetch()
3965
def _rpc_heads_to_fetch(self):
3966
response = self._call('Branch.heads_to_fetch', self._remote_path())
3967
if len(response) != 2:
3968
raise errors.UnexpectedSmartServerResponse(response)
3969
must_fetch, if_present_fetch = response
3970
return set(must_fetch), set(if_present_fetch)
3972
def _vfs_heads_to_fetch(self):
2715
3973
self._ensure_real()
2716
return self._real_branch.set_push_location(location)
3974
return self._real_branch.heads_to_fetch()
2719
3977
class RemoteConfig(object):
2774
4042
medium = self._branch._client._medium
2775
4043
if medium._is_remote_before((1, 14)):
2776
4044
return self._vfs_set_option(value, name, section)
4045
if isinstance(value, dict):
4046
if medium._is_remote_before((2, 2)):
4047
return self._vfs_set_option(value, name, section)
4048
return self._set_config_option_dict(value, name, section)
4050
return self._set_config_option(value, name, section)
4052
def _set_config_option(self, value, name, section):
2778
4054
path = self._branch._remote_path()
2779
4055
response = self._branch._client.call('Branch.set_config_option',
2780
4056
path, self._branch._lock_token, self._branch._repo_lock_token,
2781
4057
value.encode('utf8'), name, section or '')
2782
4058
except errors.UnknownSmartMethod:
4059
medium = self._branch._client._medium
2783
4060
medium._remember_remote_is_before((1, 14))
2784
4061
return self._vfs_set_option(value, name, section)
2785
4062
if response != ():
2786
4063
raise errors.UnexpectedSmartServerResponse(response)
4065
def _serialize_option_dict(self, option_dict):
4067
for key, value in option_dict.items():
4068
if isinstance(key, unicode):
4069
key = key.encode('utf8')
4070
if isinstance(value, unicode):
4071
value = value.encode('utf8')
4072
utf8_dict[key] = value
4073
return bencode.bencode(utf8_dict)
4075
def _set_config_option_dict(self, value, name, section):
4077
path = self._branch._remote_path()
4078
serialised_dict = self._serialize_option_dict(value)
4079
response = self._branch._client.call(
4080
'Branch.set_config_option_dict',
4081
path, self._branch._lock_token, self._branch._repo_lock_token,
4082
serialised_dict, name, section or '')
4083
except errors.UnknownSmartMethod:
4084
medium = self._branch._client._medium
4085
medium._remember_remote_is_before((2, 2))
4086
return self._vfs_set_option(value, name, section)
4088
raise errors.UnexpectedSmartServerResponse(response)
2788
4090
def _real_object(self):
2789
4091
self._branch._ensure_real()
2790
4092
return self._branch._real_branch
2867
4163
return context['path']
2868
except KeyError, key_err:
4164
except KeyError as key_err:
2870
4166
return err.error_args[0]
2871
except IndexError, idx_err:
4167
except IndexError as idx_err:
2873
4169
'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'):
4173
translator = error_translators.get(err.error_verb)
4177
raise translator(err, find, get_path)
4179
translator = no_context_error_translators.get(err.error_verb)
4181
raise errors.UnknownErrorFromSmartServer(err)
4183
raise translator(err)
4186
error_translators.register('NoSuchRevision',
4187
lambda err, find, get_path: NoSuchRevision(
4188
find('branch'), err.error_args[0]))
4189
error_translators.register('nosuchrevision',
4190
lambda err, find, get_path: NoSuchRevision(
4191
find('repository'), err.error_args[0]))
4193
def _translate_nobranch_error(err, find, get_path):
4194
if len(err.error_args) >= 1:
4195
extra = err.error_args[0]
4198
return errors.NotBranchError(path=find('bzrdir').root_transport.base,
4201
error_translators.register('nobranch', _translate_nobranch_error)
4202
error_translators.register('norepository',
4203
lambda err, find, get_path: errors.NoRepositoryPresent(
4205
error_translators.register('UnlockableTransport',
4206
lambda err, find, get_path: errors.UnlockableTransport(
4207
find('bzrdir').root_transport))
4208
error_translators.register('TokenMismatch',
4209
lambda err, find, get_path: errors.TokenMismatch(
4210
find('token'), '(remote token)'))
4211
error_translators.register('Diverged',
4212
lambda err, find, get_path: errors.DivergedBranches(
4213
find('branch'), find('other_branch')))
4214
error_translators.register('NotStacked',
4215
lambda err, find, get_path: errors.NotStacked(branch=find('branch')))
4217
def _translate_PermissionDenied(err, find, get_path):
4219
if len(err.error_args) >= 2:
4220
extra = err.error_args[1]
4223
return errors.PermissionDenied(path, extra=extra)
4225
error_translators.register('PermissionDenied', _translate_PermissionDenied)
4226
error_translators.register('ReadError',
4227
lambda err, find, get_path: errors.ReadError(get_path()))
4228
error_translators.register('NoSuchFile',
4229
lambda err, find, get_path: errors.NoSuchFile(get_path()))
4230
error_translators.register('TokenLockingNotSupported',
4231
lambda err, find, get_path: errors.TokenLockingNotSupported(
4232
find('repository')))
4233
error_translators.register('UnsuspendableWriteGroup',
4234
lambda err, find, get_path: errors.UnsuspendableWriteGroup(
4235
repository=find('repository')))
4236
error_translators.register('UnresumableWriteGroup',
4237
lambda err, find, get_path: errors.UnresumableWriteGroup(
4238
repository=find('repository'), write_groups=err.error_args[0],
4239
reason=err.error_args[1]))
4240
no_context_error_translators.register('IncompatibleRepositories',
4241
lambda err: errors.IncompatibleRepositories(
4242
err.error_args[0], err.error_args[1], err.error_args[2]))
4243
no_context_error_translators.register('LockContention',
4244
lambda err: errors.LockContention('(remote lock)'))
4245
no_context_error_translators.register('LockFailed',
4246
lambda err: errors.LockFailed(err.error_args[0], err.error_args[1]))
4247
no_context_error_translators.register('TipChangeRejected',
4248
lambda err: errors.TipChangeRejected(err.error_args[0].decode('utf8')))
4249
no_context_error_translators.register('UnstackableBranchFormat',
4250
lambda err: errors.UnstackableBranchFormat(*err.error_args))
4251
no_context_error_translators.register('UnstackableRepositoryFormat',
4252
lambda err: errors.UnstackableRepositoryFormat(*err.error_args))
4253
no_context_error_translators.register('FileExists',
4254
lambda err: errors.FileExists(err.error_args[0]))
4255
no_context_error_translators.register('DirectoryNotEmpty',
4256
lambda err: errors.DirectoryNotEmpty(err.error_args[0]))
4258
def _translate_short_readv_error(err):
4259
args = err.error_args
4260
return errors.ShortReadvError(args[0], int(args[1]), int(args[2]),
4263
no_context_error_translators.register('ShortReadvError',
4264
_translate_short_readv_error)
4266
def _translate_unicode_error(err):
2932
4267
encoding = str(err.error_args[0]) # encoding must always be a string
2933
4268
val = err.error_args[1]
2934
4269
start = int(err.error_args[2])