89
# Note: RemoteBzrDirFormat is in bzrdir.py
91
class RemoteBzrDir(BzrDir, _RpcHelper):
115
# Note that RemoteBzrDirProber lives in breezy.bzrdir so breezy.remote
116
# does not have to be imported unless a remote format is involved.
118
class RemoteBzrDirFormat(_mod_bzrdir.BzrDirMetaFormat1):
119
"""Format representing bzrdirs accessed via a smart server"""
121
supports_workingtrees = False
123
colocated_branches = False
126
_mod_bzrdir.BzrDirMetaFormat1.__init__(self)
127
# XXX: It's a bit ugly that the network name is here, because we'd
128
# like to believe that format objects are stateless or at least
129
# immutable, However, we do at least avoid mutating the name after
130
# it's returned. See <https://bugs.launchpad.net/bzr/+bug/504102>
131
self._network_name = None
134
return "%s(_network_name=%r)" % (self.__class__.__name__,
137
def get_format_description(self):
138
if self._network_name:
140
real_format = controldir.network_format_registry.get(
145
return 'Remote: ' + real_format.get_format_description()
146
return 'bzr remote bzrdir'
148
def get_format_string(self):
149
raise NotImplementedError(self.get_format_string)
151
def network_name(self):
152
if self._network_name:
153
return self._network_name
155
raise AssertionError("No network name set.")
157
def initialize_on_transport(self, transport):
159
# hand off the request to the smart server
160
client_medium = transport.get_smart_medium()
161
except errors.NoSmartMedium:
162
# TODO: lookup the local format from a server hint.
163
local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
164
return local_dir_format.initialize_on_transport(transport)
165
client = _SmartClient(client_medium)
166
path = client.remote_path_from_transport(transport)
168
response = client.call('BzrDirFormat.initialize', path)
169
except errors.ErrorFromSmartServer as err:
170
_translate_error(err, path=path)
171
if response[0] != 'ok':
172
raise errors.SmartProtocolError('unexpected response code %s' % (response,))
173
format = RemoteBzrDirFormat()
174
self._supply_sub_formats_to(format)
175
return RemoteBzrDir(transport, format)
177
def parse_NoneTrueFalse(self, arg):
184
raise AssertionError("invalid arg %r" % arg)
186
def _serialize_NoneTrueFalse(self, arg):
193
def _serialize_NoneString(self, arg):
196
def initialize_on_transport_ex(self, transport, use_existing_dir=False,
197
create_prefix=False, force_new_repo=False, stacked_on=None,
198
stack_on_pwd=None, repo_format_name=None, make_working_trees=None,
201
# hand off the request to the smart server
202
client_medium = transport.get_smart_medium()
203
except errors.NoSmartMedium:
206
# Decline to open it if the server doesn't support our required
207
# version (3) so that the VFS-based transport will do it.
208
if client_medium.should_probe():
210
server_version = client_medium.protocol_version()
211
if server_version != '2':
215
except errors.SmartProtocolError:
216
# Apparently there's no usable smart server there, even though
217
# the medium supports the smart protocol.
222
client = _SmartClient(client_medium)
223
path = client.remote_path_from_transport(transport)
224
if client_medium._is_remote_before((1, 16)):
227
# TODO: lookup the local format from a server hint.
228
local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
229
self._supply_sub_formats_to(local_dir_format)
230
return local_dir_format.initialize_on_transport_ex(transport,
231
use_existing_dir=use_existing_dir, create_prefix=create_prefix,
232
force_new_repo=force_new_repo, stacked_on=stacked_on,
233
stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
234
make_working_trees=make_working_trees, shared_repo=shared_repo,
236
return self._initialize_on_transport_ex_rpc(client, path, transport,
237
use_existing_dir, create_prefix, force_new_repo, stacked_on,
238
stack_on_pwd, repo_format_name, make_working_trees, shared_repo)
240
def _initialize_on_transport_ex_rpc(self, client, path, transport,
241
use_existing_dir, create_prefix, force_new_repo, stacked_on,
242
stack_on_pwd, repo_format_name, make_working_trees, shared_repo):
244
args.append(self._serialize_NoneTrueFalse(use_existing_dir))
245
args.append(self._serialize_NoneTrueFalse(create_prefix))
246
args.append(self._serialize_NoneTrueFalse(force_new_repo))
247
args.append(self._serialize_NoneString(stacked_on))
248
# stack_on_pwd is often/usually our transport
251
stack_on_pwd = transport.relpath(stack_on_pwd)
254
except errors.PathNotChild:
256
args.append(self._serialize_NoneString(stack_on_pwd))
257
args.append(self._serialize_NoneString(repo_format_name))
258
args.append(self._serialize_NoneTrueFalse(make_working_trees))
259
args.append(self._serialize_NoneTrueFalse(shared_repo))
260
request_network_name = self._network_name or \
261
_mod_bzrdir.BzrDirFormat.get_default_format().network_name()
263
response = client.call('BzrDirFormat.initialize_ex_1.16',
264
request_network_name, path, *args)
265
except errors.UnknownSmartMethod:
266
client._medium._remember_remote_is_before((1,16))
267
local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
268
self._supply_sub_formats_to(local_dir_format)
269
return local_dir_format.initialize_on_transport_ex(transport,
270
use_existing_dir=use_existing_dir, create_prefix=create_prefix,
271
force_new_repo=force_new_repo, stacked_on=stacked_on,
272
stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
273
make_working_trees=make_working_trees, shared_repo=shared_repo,
275
except errors.ErrorFromSmartServer as err:
276
_translate_error(err, path=path)
277
repo_path = response[0]
278
bzrdir_name = response[6]
279
require_stacking = response[7]
280
require_stacking = self.parse_NoneTrueFalse(require_stacking)
281
format = RemoteBzrDirFormat()
282
format._network_name = bzrdir_name
283
self._supply_sub_formats_to(format)
284
bzrdir = RemoteBzrDir(transport, format, _client=client)
286
repo_format = response_tuple_to_repo_format(response[1:])
290
repo_bzrdir_format = RemoteBzrDirFormat()
291
repo_bzrdir_format._network_name = response[5]
292
repo_bzr = RemoteBzrDir(transport.clone(repo_path),
296
final_stack = response[8] or None
297
final_stack_pwd = response[9] or None
299
final_stack_pwd = urlutils.join(
300
transport.base, final_stack_pwd)
301
remote_repo = RemoteRepository(repo_bzr, repo_format)
302
if len(response) > 10:
303
# Updated server verb that locks remotely.
304
repo_lock_token = response[10] or None
305
remote_repo.lock_write(repo_lock_token, _skip_rpc=True)
307
remote_repo.dont_leave_lock_in_place()
309
remote_repo.lock_write()
310
policy = _mod_bzrdir.UseExistingRepository(remote_repo, final_stack,
311
final_stack_pwd, require_stacking)
312
policy.acquire_repository()
316
bzrdir._format.set_branch_format(self.get_branch_format())
318
# The repo has already been created, but we need to make sure that
319
# we'll make a stackable branch.
320
bzrdir._format.require_stacking(_skip_repo=True)
321
return remote_repo, bzrdir, require_stacking, policy
323
def _open(self, transport):
324
return RemoteBzrDir(transport, self)
326
def __eq__(self, other):
327
if not isinstance(other, RemoteBzrDirFormat):
329
return self.get_format_description() == other.get_format_description()
331
def __return_repository_format(self):
332
# Always return a RemoteRepositoryFormat object, but if a specific bzr
333
# repository format has been asked for, tell the RemoteRepositoryFormat
334
# that it should use that for init() etc.
335
result = RemoteRepositoryFormat()
336
custom_format = getattr(self, '_repository_format', None)
338
if isinstance(custom_format, RemoteRepositoryFormat):
341
# We will use the custom format to create repositories over the
342
# wire; expose its details like rich_root_data for code to
344
result._custom_format = custom_format
347
def get_branch_format(self):
348
result = _mod_bzrdir.BzrDirMetaFormat1.get_branch_format(self)
349
if not isinstance(result, RemoteBranchFormat):
350
new_result = RemoteBranchFormat()
351
new_result._custom_format = result
353
self.set_branch_format(new_result)
357
repository_format = property(__return_repository_format,
358
_mod_bzrdir.BzrDirMetaFormat1._set_repository_format) #.im_func)
361
class RemoteControlStore(_mod_config.IniFileStore):
362
"""Control store which attempts to use HPSS calls to retrieve control store.
364
Note that this is specific to bzr-based formats.
367
def __init__(self, bzrdir):
368
super(RemoteControlStore, self).__init__()
369
self.controldir = bzrdir
370
self._real_store = None
372
def lock_write(self, token=None):
374
return self._real_store.lock_write(token)
378
return self._real_store.unlock()
382
# We need to be able to override the undecorated implementation
383
self.save_without_locking()
385
def save_without_locking(self):
386
super(RemoteControlStore, self).save()
388
def _ensure_real(self):
389
self.controldir._ensure_real()
390
if self._real_store is None:
391
self._real_store = _mod_config.ControlStore(self.controldir)
393
def external_url(self):
394
return urlutils.join(self.branch.user_url, 'control.conf')
396
def _load_content(self):
397
medium = self.controldir._client._medium
398
path = self.controldir._path_for_remote_call(self.controldir._client)
400
response, handler = self.controldir._call_expecting_body(
401
'BzrDir.get_config_file', path)
402
except errors.UnknownSmartMethod:
404
return self._real_store._load_content()
405
if len(response) and response[0] != 'ok':
406
raise errors.UnexpectedSmartServerResponse(response)
407
return handler.read_body_bytes()
409
def _save_content(self, content):
410
# FIXME JRV 2011-11-22: Ideally this should use a
411
# HPSS call too, but at the moment it is not possible
412
# to write lock control directories.
414
return self._real_store._save_content(content)
417
class RemoteBzrDir(_mod_bzrdir.BzrDir, _RpcHelper):
92
418
"""Control directory on a remote server, accessed via bzr:// or similar."""
94
420
def __init__(self, transport, format, _client=None, _force_probe=False):
266
663
def destroy_branch(self, name=None):
267
664
"""See BzrDir.destroy_branch"""
269
self._real_bzrdir.destroy_branch(name=name)
666
name = self._get_selected_branch()
668
raise errors.NoColocatedBranchSupport(self)
669
path = self._path_for_remote_call(self._client)
675
response = self._call('BzrDir.destroy_branch', path, *args)
676
except errors.UnknownSmartMethod:
678
self._real_bzrdir.destroy_branch(name=name)
679
self._next_open_branch_result = None
270
681
self._next_open_branch_result = None
682
if response[0] != 'ok':
683
raise SmartProtocolError('unexpected response code %s' % (response,))
272
def create_workingtree(self, revision_id=None, from_branch=None):
685
def create_workingtree(self, revision_id=None, from_branch=None,
686
accelerator_tree=None, hardlink=False):
273
687
raise errors.NotLocalUrl(self.transport.base)
275
def find_branch_format(self):
689
def find_branch_format(self, name=None):
276
690
"""Find the branch 'format' for this bzrdir.
278
692
This might be a synthetic object for e.g. RemoteBranch and SVN.
280
b = self.open_branch()
694
b = self.open_branch(name=name)
283
def get_branch_reference(self):
697
def get_branches(self, possible_transports=None, ignore_fallbacks=False):
698
path = self._path_for_remote_call(self._client)
700
response, handler = self._call_expecting_body(
701
'BzrDir.get_branches', path)
702
except errors.UnknownSmartMethod:
704
return self._real_bzrdir.get_branches()
705
if response[0] != "success":
706
raise errors.UnexpectedSmartServerResponse(response)
707
body = bencode.bdecode(handler.read_body_bytes())
709
for name, value in viewitems(body):
710
ret[name] = self._open_branch(name, value[0], value[1],
711
possible_transports=possible_transports,
712
ignore_fallbacks=ignore_fallbacks)
715
def set_branch_reference(self, target_branch, name=None):
716
"""See BzrDir.set_branch_reference()."""
718
name = self._get_selected_branch()
720
raise errors.NoColocatedBranchSupport(self)
722
return self._real_bzrdir.set_branch_reference(target_branch, name=name)
724
def get_branch_reference(self, name=None):
284
725
"""See BzrDir.get_branch_reference()."""
727
name = self._get_selected_branch()
729
raise errors.NoColocatedBranchSupport(self)
285
730
response = self._get_branch_reference()
286
731
if response[0] == 'ref':
287
732
return response[1]
318
763
raise errors.UnexpectedSmartServerResponse(response)
321
def _get_tree_branch(self):
766
def _get_tree_branch(self, name=None):
322
767
"""See BzrDir._get_tree_branch()."""
323
return None, self.open_branch()
768
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':
770
def _open_branch(self, name, kind, location_or_format,
771
ignore_fallbacks=False, possible_transports=None):
336
773
# a branch reference, use the existing BranchReference logic.
337
774
format = BranchReferenceFormat()
338
775
return format.open(self, name=name, _found=True,
339
location=response[1], ignore_fallbacks=ignore_fallbacks)
340
branch_format_name = response[1]
776
location=location_or_format, ignore_fallbacks=ignore_fallbacks,
777
possible_transports=possible_transports)
778
branch_format_name = location_or_format
341
779
if not branch_format_name:
342
780
branch_format_name = None
343
781
format = RemoteBranchFormat(network_name=branch_format_name)
344
782
return RemoteBranch(self, self.find_repository(), format=format,
345
setup_stacking=not ignore_fallbacks, name=name)
783
setup_stacking=not ignore_fallbacks, name=name,
784
possible_transports=possible_transports)
786
def open_branch(self, name=None, unsupported=False,
787
ignore_fallbacks=False, possible_transports=None):
789
name = self._get_selected_branch()
791
raise errors.NoColocatedBranchSupport(self)
793
raise NotImplementedError('unsupported flag support not implemented yet.')
794
if self._next_open_branch_result is not None:
795
# See create_branch for details.
796
result = self._next_open_branch_result
797
self._next_open_branch_result = None
799
response = self._get_branch_reference()
800
return self._open_branch(name, response[0], response[1],
801
possible_transports=possible_transports,
802
ignore_fallbacks=ignore_fallbacks)
347
804
def _open_repo_v1(self, path):
348
805
verb = 'BzrDir.find_repository'
529
1010
self._custom_format.supports_tree_reference
530
1011
return self._supports_tree_reference
532
def _vfs_initialize(self, a_bzrdir, shared):
1014
def revision_graph_can_have_wrong_parents(self):
1015
if self._revision_graph_can_have_wrong_parents is None:
1017
self._revision_graph_can_have_wrong_parents = \
1018
self._custom_format.revision_graph_can_have_wrong_parents
1019
return self._revision_graph_can_have_wrong_parents
1021
def _vfs_initialize(self, a_controldir, shared):
533
1022
"""Helper for common code in initialize."""
534
1023
if self._custom_format:
535
1024
# Custom format requested
536
result = self._custom_format.initialize(a_bzrdir, shared=shared)
1025
result = self._custom_format.initialize(a_controldir, shared=shared)
537
1026
elif self._creating_bzrdir is not None:
538
1027
# Use the format that the repository we were created to back
540
1029
prior_repo = self._creating_bzrdir.open_repository()
541
1030
prior_repo._ensure_real()
542
1031
result = prior_repo._real_repository._format.initialize(
543
a_bzrdir, shared=shared)
1032
a_controldir, shared=shared)
545
1034
# assume that a_bzr is a RemoteBzrDir but the smart server didn't
546
1035
# support remote initialization.
547
1036
# We delegate to a real object at this point (as RemoteBzrDir
548
1037
# 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)
1038
# recursion if we just called a_controldir.create_repository.
1039
a_controldir._ensure_real()
1040
result = a_controldir._real_bzrdir.create_repository(shared=shared)
552
1041
if not isinstance(result, RemoteRepository):
553
return self.open(a_bzrdir)
1042
return self.open(a_controldir)
557
def initialize(self, a_bzrdir, shared=False):
1046
def initialize(self, a_controldir, shared=False):
558
1047
# 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
1048
if not isinstance(a_controldir, RemoteBzrDir):
1049
return self._vfs_initialize(a_controldir, shared)
1050
medium = a_controldir._client._medium
562
1051
if medium._is_remote_before((1, 13)):
563
return self._vfs_initialize(a_bzrdir, shared)
1052
return self._vfs_initialize(a_controldir, shared)
564
1053
# Creating on a remote bzr dir.
565
1054
# 1) get the network name to use.
566
1055
if self._custom_format:
568
1057
elif self._network_name:
569
1058
network_name = self._network_name
571
# Select the current bzrlib default and ask for that.
572
reference_bzrdir_format = bzrdir.format_registry.get('default')()
1060
# Select the current breezy default and ask for that.
1061
reference_bzrdir_format = controldir.format_registry.get('default')()
573
1062
reference_format = reference_bzrdir_format.repository_format
574
1063
network_name = reference_format.network_name()
575
1064
# 2) try direct creation via RPC
576
path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
1065
path = a_controldir._path_for_remote_call(a_controldir._client)
577
1066
verb = 'BzrDir.create_repository'
579
1068
shared_str = 'True'
581
1070
shared_str = 'False'
583
response = a_bzrdir._call(verb, path, network_name, shared_str)
1072
response = a_controldir._call(verb, path, network_name, shared_str)
584
1073
except errors.UnknownSmartMethod:
585
1074
# Fallback - use vfs methods
586
1075
medium._remember_remote_is_before((1, 13))
587
return self._vfs_initialize(a_bzrdir, shared)
1076
return self._vfs_initialize(a_controldir, shared)
589
1078
# Turn the response into a RemoteRepository object.
590
1079
format = response_tuple_to_repo_format(response[1:])
591
1080
# Used to support creating a real format instance when needed.
592
format._creating_bzrdir = a_bzrdir
593
remote_repo = RemoteRepository(a_bzrdir, format)
1081
format._creating_bzrdir = a_controldir
1082
remote_repo = RemoteRepository(a_controldir, format)
594
1083
format._creating_repo = remote_repo
595
1084
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()
1086
def open(self, a_controldir):
1087
if not isinstance(a_controldir, RemoteBzrDir):
1088
raise AssertionError('%r is not a RemoteBzrDir' % (a_controldir,))
1089
return a_controldir.open_repository()
602
1091
def _ensure_real(self):
603
1092
if self._custom_format is None:
604
self._custom_format = repository.network_format_registry.get(
1094
self._custom_format = _mod_repository.network_format_registry.get(
1097
raise errors.UnknownFormatError(kind='repository',
1098
format=self._network_name)
608
1101
def _fetch_order(self):
1195
1775
raise errors.UnexpectedSmartServerResponse(response)
1197
1778
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,
1779
"""Create a descendent repository for new development.
1781
Unlike clone, this does not copy the settings of the repository.
1783
dest_repo = self._create_sprouting_repo(to_bzrdir, shared=False)
1202
1784
dest_repo.fetch(self, revision_id=revision_id)
1203
1785
return dest_repo
1787
def _create_sprouting_repo(self, a_controldir, shared):
1788
if not isinstance(a_controldir._format, self.controldir._format.__class__):
1789
# use target default format.
1790
dest_repo = a_controldir.create_repository()
1792
# Most control formats need the repository to be specifically
1793
# created, but on some old all-in-one formats it's not needed
1795
dest_repo = self._format.initialize(a_controldir, shared=shared)
1796
except errors.UninitializableFormat:
1797
dest_repo = a_controldir.open_repository()
1205
1800
### These methods are just thin shims to the VFS object for now.
1207
1803
def revision_tree(self, revision_id):
1209
return self._real_repository.revision_tree(revision_id)
1804
revision_id = _mod_revision.ensure_null(revision_id)
1805
if revision_id == _mod_revision.NULL_REVISION:
1806
return InventoryRevisionTree(self,
1807
Inventory(root_id=None), _mod_revision.NULL_REVISION)
1809
return list(self.revision_trees([revision_id]))[0]
1211
1811
def get_serializer_format(self):
1213
return self._real_repository.get_serializer_format()
1812
path = self.controldir._path_for_remote_call(self._client)
1814
response = self._call('VersionedFileRepository.get_serializer_format',
1816
except errors.UnknownSmartMethod:
1818
return self._real_repository.get_serializer_format()
1819
if response[0] != 'ok':
1820
raise errors.UnexpectedSmartServerResponse(response)
1215
1823
def get_commit_builder(self, branch, parents, config, timestamp=None,
1216
1824
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)
1825
revision_id=None, lossy=False):
1826
"""Obtain a CommitBuilder for this repository.
1828
:param branch: Branch to commit to.
1829
:param parents: Revision ids of the parents of the new revision.
1830
:param config: Configuration to use.
1831
:param timestamp: Optional timestamp recorded for commit.
1832
:param timezone: Optional timezone for timestamp.
1833
:param committer: Optional committer to set for commit.
1834
:param revprops: Optional dictionary of revision properties.
1835
:param revision_id: Optional revision id.
1836
:param lossy: Whether to discard data that can not be natively
1837
represented, when pushing to a foreign VCS
1839
if self._fallback_repositories and not self._format.supports_chks:
1840
raise errors.BzrError("Cannot commit directly to a stacked branch"
1841
" in pre-2a formats. See "
1842
"https://bugs.launchpad.net/bzr/+bug/375013 for details.")
1843
if self._format.rich_root_data:
1844
commit_builder_kls = vf_repository.VersionedFileRootCommitBuilder
1846
commit_builder_kls = vf_repository.VersionedFileCommitBuilder
1847
result = commit_builder_kls(self, parents, config,
1848
timestamp, timezone, committer, revprops, revision_id,
1850
self.start_write_group()
1227
1853
def add_fallback_repository(self, repository):
1228
1854
"""Add a repository to use for looking up data not held locally.
1272
1899
delta, new_revision_id, parents, basis_inv=basis_inv,
1273
1900
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)
1902
def add_revision(self, revision_id, rev, inv=None):
1903
_mod_revision.check_not_reserved_id(revision_id)
1904
key = (revision_id,)
1905
# check inventory present
1906
if not self.inventories.get_parent_map([key]):
1908
raise errors.WeaveRevisionNotPresent(revision_id,
1911
# yes, this is not suitable for adding with ghosts.
1912
rev.inventory_sha1 = self.add_inventory(revision_id, inv,
1915
rev.inventory_sha1 = self.inventories.get_sha1s([key])[key]
1916
self._add_revision(rev)
1918
def _add_revision(self, rev):
1919
if self._real_repository is not None:
1920
return self._real_repository._add_revision(rev)
1921
text = self._serializer.write_revision_to_string(rev)
1922
key = (rev.revision_id,)
1923
parents = tuple((parent,) for parent in rev.parent_ids)
1924
self._write_group_tokens, missing_keys = self._get_sink().insert_stream(
1925
[('revisions', [FulltextContentFactory(key, parents, None, text)])],
1926
self._format, self._write_group_tokens)
1280
1928
@needs_read_lock
1281
1929
def get_inventory(self, revision_id):
1930
return list(self.iter_inventories([revision_id]))[0]
1932
def _iter_inventories_rpc(self, revision_ids, ordering):
1933
if ordering is None:
1934
ordering = 'unordered'
1935
path = self.controldir._path_for_remote_call(self._client)
1936
body = "\n".join(revision_ids)
1937
response_tuple, response_handler = (
1938
self._call_with_body_bytes_expecting_body(
1939
"VersionedFileRepository.get_inventories",
1940
(path, ordering), body))
1941
if response_tuple[0] != "ok":
1942
raise errors.UnexpectedSmartServerResponse(response_tuple)
1943
deserializer = inventory_delta.InventoryDeltaDeserializer()
1944
byte_stream = response_handler.read_streamed_body()
1945
decoded = smart_repo._byte_stream_to_stream(byte_stream)
1947
# no results whatsoever
1949
src_format, stream = decoded
1950
if src_format.network_name() != self._format.network_name():
1951
raise AssertionError(
1952
"Mismatched RemoteRepository and stream src %r, %r" % (
1953
src_format.network_name(), self._format.network_name()))
1954
# ignore the src format, it's not really relevant
1955
prev_inv = Inventory(root_id=None,
1956
revision_id=_mod_revision.NULL_REVISION)
1957
# there should be just one substream, with inventory deltas
1958
substream_kind, substream = next(stream)
1959
if substream_kind != "inventory-deltas":
1960
raise AssertionError(
1961
"Unexpected stream %r received" % substream_kind)
1962
for record in substream:
1963
(parent_id, new_id, versioned_root, tree_references, invdelta) = (
1964
deserializer.parse_text_bytes(record.get_bytes_as("fulltext")))
1965
if parent_id != prev_inv.revision_id:
1966
raise AssertionError("invalid base %r != %r" % (parent_id,
1967
prev_inv.revision_id))
1968
inv = prev_inv.create_by_apply_delta(invdelta, new_id)
1969
yield inv, inv.revision_id
1972
def _iter_inventories_vfs(self, revision_ids, ordering=None):
1282
1973
self._ensure_real()
1283
return self._real_repository.get_inventory(revision_id)
1974
return self._real_repository._iter_inventories(revision_ids, ordering)
1285
1976
def iter_inventories(self, revision_ids, ordering=None):
1287
return self._real_repository.iter_inventories(revision_ids, ordering)
1977
"""Get many inventories by revision_ids.
1979
This will buffer some or all of the texts used in constructing the
1980
inventories in memory, but will only parse a single inventory at a
1983
:param revision_ids: The expected revision ids of the inventories.
1984
:param ordering: optional ordering, e.g. 'topological'. If not
1985
specified, the order of revision_ids will be preserved (by
1986
buffering if necessary).
1987
:return: An iterator of inventories.
1989
if ((None in revision_ids)
1990
or (_mod_revision.NULL_REVISION in revision_ids)):
1991
raise ValueError('cannot get null revision inventory')
1992
for inv, revid in self._iter_inventories(revision_ids, ordering):
1994
raise errors.NoSuchRevision(self, revid)
1997
def _iter_inventories(self, revision_ids, ordering=None):
1998
if len(revision_ids) == 0:
2000
missing = set(revision_ids)
2001
if ordering is None:
2002
order_as_requested = True
2004
order = list(revision_ids)
2006
next_revid = order.pop()
2008
order_as_requested = False
2009
if ordering != 'unordered' and self._fallback_repositories:
2010
raise ValueError('unsupported ordering %r' % ordering)
2011
iter_inv_fns = [self._iter_inventories_rpc] + [
2012
fallback._iter_inventories for fallback in
2013
self._fallback_repositories]
2015
for iter_inv in iter_inv_fns:
2016
request = [revid for revid in revision_ids if revid in missing]
2017
for inv, revid in iter_inv(request, ordering):
2020
missing.remove(inv.revision_id)
2021
if ordering != 'unordered':
2025
if order_as_requested:
2026
# Yield as many results as we can while preserving order.
2027
while next_revid in invs:
2028
inv = invs.pop(next_revid)
2029
yield inv, inv.revision_id
2031
next_revid = order.pop()
2033
# We still want to fully consume the stream, just
2034
# in case it is not actually finished at this point
2037
except errors.UnknownSmartMethod:
2038
for inv, revid in self._iter_inventories_vfs(revision_ids, ordering):
2042
if order_as_requested:
2043
if next_revid is not None:
2044
yield None, next_revid
2047
yield invs.get(revid), revid
2050
yield None, missing.pop()
1289
2052
@needs_read_lock
1290
2053
def get_revision(self, revision_id):
1292
return self._real_repository.get_revision(revision_id)
2054
return self.get_revisions([revision_id])[0]
1294
2056
def get_transaction(self):
1295
2057
self._ensure_real()
1296
2058
return self._real_repository.get_transaction()
1298
2060
@needs_read_lock
1299
def clone(self, a_bzrdir, revision_id=None):
1301
return self._real_repository.clone(a_bzrdir, revision_id=revision_id)
2061
def clone(self, a_controldir, revision_id=None):
2062
dest_repo = self._create_sprouting_repo(
2063
a_controldir, shared=self.is_shared())
2064
self.copy_content_into(dest_repo, revision_id)
1303
2067
def make_working_trees(self):
1304
2068
"""See Repository.make_working_trees"""
1306
return self._real_repository.make_working_trees()
2069
path = self.controldir._path_for_remote_call(self._client)
2071
response = self._call('Repository.make_working_trees', path)
2072
except errors.UnknownSmartMethod:
2074
return self._real_repository.make_working_trees()
2075
if response[0] not in ('yes', 'no'):
2076
raise SmartProtocolError('unexpected response code %s' % (response,))
2077
return response[0] == 'yes'
1308
2079
def refresh_data(self):
1309
"""Re-read any data needed to to synchronise with disk.
2080
"""Re-read any data needed to synchronise with disk.
1311
2082
This method is intended to be called after another repository instance
1312
2083
(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.
2084
repository. On all repositories this will work outside of write groups.
2085
Some repository formats (pack and newer for breezy native formats)
2086
support refresh_data inside write groups. If called inside a write
2087
group on a repository that does not support refreshing in a write group
2088
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
2090
if self._real_repository is not None:
1320
2091
self._real_repository.refresh_data()
2092
# Refresh the parents cache for this object
2093
self._unstacked_provider.disable_cache()
2094
self._unstacked_provider.enable_cache()
1322
2096
def revision_ids_to_search_result(self, result_set):
1323
2097
"""Convert a set of revision ids to a graph SearchResult."""
1324
2098
result_parents = set()
1325
for parents in self.get_graph().get_parent_map(
1326
result_set).itervalues():
2099
for parents in viewvalues(self.get_graph().get_parent_map(result_set)):
1327
2100
result_parents.update(parents)
1328
2101
included_keys = result_set.intersection(result_parents)
1329
2102
start_keys = result_set.difference(included_keys)
1330
2103
exclude_keys = result_parents.difference(result_set)
1331
result = graph.SearchResult(start_keys, exclude_keys,
2104
result = vf_search.SearchResult(start_keys, exclude_keys,
1332
2105
len(result_set), result_set)
1335
2108
@needs_read_lock
1336
def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
2109
def search_missing_revision_ids(self, other,
2110
find_ghosts=True, revision_ids=None, if_present_ids=None,
1337
2112
"""Return the revision ids that other has that this does not.
1339
2114
These are returned in topological order.
1341
2116
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)
2118
inter_repo = _mod_repository.InterRepository.get(other, self)
2119
return inter_repo.search_missing_revision_ids(
2120
find_ghosts=find_ghosts, revision_ids=revision_ids,
2121
if_present_ids=if_present_ids, limit=limit)
1346
def fetch(self, source, revision_id=None, pb=None, find_ghosts=False,
2123
def fetch(self, source, revision_id=None, find_ghosts=False,
1347
2124
fetch_spec=None):
1348
2125
# No base implementation to use as RemoteRepository is not a subclass
1349
2126
# of Repository; so this is a copy of Repository.fetch().
1388
2164
return self._real_repository._get_versioned_file_checker(
1389
2165
revisions, revision_versions_cache)
2167
def _iter_files_bytes_rpc(self, desired_files, absent):
2168
path = self.controldir._path_for_remote_call(self._client)
2171
for (file_id, revid, identifier) in desired_files:
2172
lines.append("%s\0%s" % (
2173
osutils.safe_file_id(file_id),
2174
osutils.safe_revision_id(revid)))
2175
identifiers.append(identifier)
2176
(response_tuple, response_handler) = (
2177
self._call_with_body_bytes_expecting_body(
2178
"Repository.iter_files_bytes", (path, ), "\n".join(lines)))
2179
if response_tuple != ('ok', ):
2180
response_handler.cancel_read_body()
2181
raise errors.UnexpectedSmartServerResponse(response_tuple)
2182
byte_stream = response_handler.read_streamed_body()
2183
def decompress_stream(start, byte_stream, unused):
2184
decompressor = zlib.decompressobj()
2185
yield decompressor.decompress(start)
2186
while decompressor.unused_data == "":
2188
data = next(byte_stream)
2189
except StopIteration:
2191
yield decompressor.decompress(data)
2192
yield decompressor.flush()
2193
unused.append(decompressor.unused_data)
2196
while not "\n" in unused:
2197
unused += next(byte_stream)
2198
header, rest = unused.split("\n", 1)
2199
args = header.split("\0")
2200
if args[0] == "absent":
2201
absent[identifiers[int(args[3])]] = (args[1], args[2])
2204
elif args[0] == "ok":
2207
raise errors.UnexpectedSmartServerResponse(args)
2209
yield (identifiers[idx],
2210
decompress_stream(rest, byte_stream, unused_chunks))
2211
unused = "".join(unused_chunks)
1391
2213
def iter_files_bytes(self, desired_files):
1392
2214
"""See Repository.iter_file_bytes.
1395
return self._real_repository.iter_files_bytes(desired_files)
2218
for (identifier, bytes_iterator) in self._iter_files_bytes_rpc(
2219
desired_files, absent):
2220
yield identifier, bytes_iterator
2221
for fallback in self._fallback_repositories:
2224
desired_files = [(key[0], key[1], identifier)
2225
for identifier, key in viewitems(absent)]
2226
for (identifier, bytes_iterator) in fallback.iter_files_bytes(desired_files):
2227
del absent[identifier]
2228
yield identifier, bytes_iterator
2230
# There may be more missing items, but raise an exception
2232
missing_identifier = next(iter(absent))
2233
missing_key = absent[missing_identifier]
2234
raise errors.RevisionNotPresent(revision_id=missing_key[1],
2235
file_id=missing_key[0])
2236
except errors.UnknownSmartMethod:
2238
for (identifier, bytes_iterator) in (
2239
self._real_repository.iter_files_bytes(desired_files)):
2240
yield identifier, bytes_iterator
2242
def get_cached_parent_map(self, revision_ids):
2243
"""See breezy.CachingParentsProvider.get_cached_parent_map"""
2244
return self._unstacked_provider.get_cached_parent_map(revision_ids)
1397
2246
def get_parent_map(self, revision_ids):
1398
"""See bzrlib.Graph.get_parent_map()."""
2247
"""See breezy.Graph.get_parent_map()."""
1399
2248
return self._make_parents_provider().get_parent_map(revision_ids)
1401
2250
def _get_parent_map_rpc(self, keys):
1532
2370
@needs_read_lock
1533
2371
def get_signature_text(self, revision_id):
1535
return self._real_repository.get_signature_text(revision_id)
2372
path = self.controldir._path_for_remote_call(self._client)
2374
response_tuple, response_handler = self._call_expecting_body(
2375
'Repository.get_revision_signature_text', path, revision_id)
2376
except errors.UnknownSmartMethod:
2378
return self._real_repository.get_signature_text(revision_id)
2379
except errors.NoSuchRevision as err:
2380
for fallback in self._fallback_repositories:
2382
return fallback.get_signature_text(revision_id)
2383
except errors.NoSuchRevision:
2387
if response_tuple[0] != 'ok':
2388
raise errors.UnexpectedSmartServerResponse(response_tuple)
2389
return response_handler.read_body_bytes()
1537
2391
@needs_read_lock
1538
2392
def _get_inventory_xml(self, revision_id):
2393
# This call is used by older working tree formats,
2394
# which stored a serialized basis inventory.
1539
2395
self._ensure_real()
1540
2396
return self._real_repository._get_inventory_xml(revision_id)
1542
2399
def reconcile(self, other=None, thorough=False):
1544
return self._real_repository.reconcile(other=other, thorough=thorough)
2400
from .reconcile import RepoReconciler
2401
path = self.controldir._path_for_remote_call(self._client)
2403
response, handler = self._call_expecting_body(
2404
'Repository.reconcile', path, self._lock_token)
2405
except (errors.UnknownSmartMethod, errors.TokenLockingNotSupported):
2407
return self._real_repository.reconcile(other=other, thorough=thorough)
2408
if response != ('ok', ):
2409
raise errors.UnexpectedSmartServerResponse(response)
2410
body = handler.read_body_bytes()
2411
result = RepoReconciler(self)
2412
for line in body.split('\n'):
2415
key, val_text = line.split(':')
2416
if key == "garbage_inventories":
2417
result.garbage_inventories = int(val_text)
2418
elif key == "inconsistent_parents":
2419
result.inconsistent_parents = int(val_text)
2421
mutter("unknown reconcile key %r" % key)
1546
2424
def all_revision_ids(self):
1548
return self._real_repository.all_revision_ids()
2425
path = self.controldir._path_for_remote_call(self._client)
2427
response_tuple, response_handler = self._call_expecting_body(
2428
"Repository.all_revision_ids", path)
2429
except errors.UnknownSmartMethod:
2431
return self._real_repository.all_revision_ids()
2432
if response_tuple != ("ok", ):
2433
raise errors.UnexpectedSmartServerResponse(response_tuple)
2434
revids = set(response_handler.read_body_bytes().splitlines())
2435
for fallback in self._fallback_repositories:
2436
revids.update(set(fallback.all_revision_ids()))
2439
def _filtered_revision_trees(self, revision_ids, file_ids):
2440
"""Return Tree for a revision on this branch with only some files.
2442
:param revision_ids: a sequence of revision-ids;
2443
a revision-id may not be None or 'null:'
2444
:param file_ids: if not None, the result is filtered
2445
so that only those file-ids, their parents and their
2446
children are included.
2448
inventories = self.iter_inventories(revision_ids)
2449
for inv in inventories:
2450
# Should we introduce a FilteredRevisionTree class rather
2451
# than pre-filter the inventory here?
2452
filtered_inv = inv.filter(file_ids)
2453
yield InventoryRevisionTree(self, filtered_inv, filtered_inv.revision_id)
1550
2455
@needs_read_lock
1551
2456
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)
2457
medium = self._client._medium
2458
if medium._is_remote_before((1, 2)):
2460
for delta in self._real_repository.get_deltas_for_revisions(
2461
revisions, specific_fileids):
2464
# Get the revision-ids of interest
2465
required_trees = set()
2466
for revision in revisions:
2467
required_trees.add(revision.revision_id)
2468
required_trees.update(revision.parent_ids[:1])
2470
# Get the matching filtered trees. Note that it's more
2471
# efficient to pass filtered trees to changes_from() rather
2472
# than doing the filtering afterwards. changes_from() could
2473
# arguably do the filtering itself but it's path-based, not
2474
# file-id based, so filtering before or afterwards is
2476
if specific_fileids is None:
2477
trees = dict((t.get_revision_id(), t) for
2478
t in self.revision_trees(required_trees))
2480
trees = dict((t.get_revision_id(), t) for
2481
t in self._filtered_revision_trees(required_trees,
2484
# Calculate the deltas
2485
for revision in revisions:
2486
if not revision.parent_ids:
2487
old_tree = self.revision_tree(_mod_revision.NULL_REVISION)
2489
old_tree = trees[revision.parent_ids[0]]
2490
yield trees[revision.revision_id].changes_from(old_tree)
1556
2492
@needs_read_lock
1557
2493
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)
2494
r = self.get_revision(revision_id)
2495
return list(self.get_deltas_for_revisions([r],
2496
specific_fileids=specific_fileids))[0]
1562
2498
@needs_read_lock
1563
2499
def revision_trees(self, revision_ids):
1565
return self._real_repository.revision_trees(revision_ids)
2500
inventories = self.iter_inventories(revision_ids)
2501
for inv in inventories:
2502
yield InventoryRevisionTree(self, inv, inv.revision_id)
1567
2504
@needs_read_lock
1568
2505
def get_revision_reconcile(self, revision_id):
1680
2630
self._ensure_real()
1681
2631
return self._real_repository.texts
2633
def _iter_revisions_rpc(self, revision_ids):
2634
body = "\n".join(revision_ids)
2635
path = self.controldir._path_for_remote_call(self._client)
2636
response_tuple, response_handler = (
2637
self._call_with_body_bytes_expecting_body(
2638
"Repository.iter_revisions", (path, ), body))
2639
if response_tuple[0] != "ok":
2640
raise errors.UnexpectedSmartServerResponse(response_tuple)
2641
serializer_format = response_tuple[1]
2642
serializer = serializer_format_registry.get(serializer_format)
2643
byte_stream = response_handler.read_streamed_body()
2644
decompressor = zlib.decompressobj()
2646
for bytes in byte_stream:
2647
chunks.append(decompressor.decompress(bytes))
2648
if decompressor.unused_data != "":
2649
chunks.append(decompressor.flush())
2650
yield serializer.read_revision_from_string("".join(chunks))
2651
unused = decompressor.unused_data
2652
decompressor = zlib.decompressobj()
2653
chunks = [decompressor.decompress(unused)]
2654
chunks.append(decompressor.flush())
2655
text = "".join(chunks)
2657
yield serializer.read_revision_from_string("".join(chunks))
1683
2659
@needs_read_lock
1684
2660
def get_revisions(self, revision_ids):
1686
return self._real_repository.get_revisions(revision_ids)
2661
if revision_ids is None:
2662
revision_ids = self.all_revision_ids()
2664
for rev_id in revision_ids:
2665
if not rev_id or not isinstance(rev_id, basestring):
2666
raise errors.InvalidRevisionId(
2667
revision_id=rev_id, branch=self)
2669
missing = set(revision_ids)
2671
for rev in self._iter_revisions_rpc(revision_ids):
2672
missing.remove(rev.revision_id)
2673
revs[rev.revision_id] = rev
2674
except errors.UnknownSmartMethod:
2676
return self._real_repository.get_revisions(revision_ids)
2677
for fallback in self._fallback_repositories:
2680
for revid in list(missing):
2681
# XXX JRV 2011-11-20: It would be nice if there was a
2682
# public method on Repository that could be used to query
2683
# for revision objects *without* failing completely if one
2684
# was missing. There is VersionedFileRepository._iter_revisions,
2685
# but unfortunately that's private and not provided by
2686
# all repository implementations.
2688
revs[revid] = fallback.get_revision(revid)
2689
except errors.NoSuchRevision:
2692
missing.remove(revid)
2694
raise errors.NoSuchRevision(self, list(missing)[0])
2695
return [revs[revid] for revid in revision_ids]
1688
2697
def supports_rich_root(self):
1689
2698
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
2701
def _serializer(self):
1697
2702
return self._format._serializer
1699
2705
def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
1701
return self._real_repository.store_revision_signature(
1702
gpg_strategy, plaintext, revision_id)
2706
signature = gpg_strategy.sign(plaintext)
2707
self.add_signature_text(revision_id, signature)
1704
2709
def add_signature_text(self, revision_id, signature):
1706
return self._real_repository.add_signature_text(revision_id, signature)
2710
if self._real_repository:
2711
# If there is a real repository the write group will
2712
# be in the real repository as well, so use that:
2714
return self._real_repository.add_signature_text(
2715
revision_id, signature)
2716
path = self.controldir._path_for_remote_call(self._client)
2717
response, handler = self._call_with_body_bytes_expecting_body(
2718
'Repository.add_signature_text', (path, self._lock_token,
2719
revision_id) + tuple(self._write_group_tokens), signature)
2720
handler.cancel_read_body()
2722
if response[0] != 'ok':
2723
raise errors.UnexpectedSmartServerResponse(response)
2724
self._write_group_tokens = response[1:]
1708
2726
def has_signature_for_revision_id(self, revision_id):
1710
return self._real_repository.has_signature_for_revision_id(revision_id)
2727
path = self.controldir._path_for_remote_call(self._client)
2729
response = self._call('Repository.has_signature_for_revision_id',
2731
except errors.UnknownSmartMethod:
2733
return self._real_repository.has_signature_for_revision_id(
2735
if response[0] not in ('yes', 'no'):
2736
raise SmartProtocolError('unexpected response code %s' % (response,))
2737
if response[0] == 'yes':
2739
for fallback in self._fallback_repositories:
2740
if fallback.has_signature_for_revision_id(revision_id):
2745
def verify_revision_signature(self, revision_id, gpg_strategy):
2746
if not self.has_signature_for_revision_id(revision_id):
2747
return gpg.SIGNATURE_NOT_SIGNED, None
2748
signature = self.get_signature_text(revision_id)
2750
testament = _mod_testament.Testament.from_revision(self, revision_id)
2751
plaintext = testament.as_short_text()
2753
return gpg_strategy.verify(signature, plaintext)
1712
2755
def item_keys_introduced_by(self, revision_ids, _files_pb=None):
1713
2756
self._ensure_real()
1714
2757
return self._real_repository.item_keys_introduced_by(revision_ids,
1715
2758
_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
2760
def _find_inconsistent_revision_parents(self, revisions_iterator=None):
1723
2761
self._ensure_real()
1724
2762
return self._real_repository._find_inconsistent_revision_parents(
2059
3108
def network_name(self):
2060
3109
return self._network_name
2062
def open(self, a_bzrdir, name=None, ignore_fallbacks=False):
2063
return a_bzrdir.open_branch(name=name,
3111
def open(self, a_controldir, name=None, ignore_fallbacks=False):
3112
return a_controldir.open_branch(name=name,
2064
3113
ignore_fallbacks=ignore_fallbacks)
2066
def _vfs_initialize(self, a_bzrdir, name):
3115
def _vfs_initialize(self, a_controldir, name, append_revisions_only,
2067
3117
# Initialisation when using a local bzrdir object, or a non-vfs init
2068
3118
# method is not available on the server.
2069
3119
# 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,
3121
if isinstance(a_controldir, RemoteBzrDir):
3122
a_controldir._ensure_real()
3123
result = self._custom_format.initialize(a_controldir._real_bzrdir,
3124
name=name, append_revisions_only=append_revisions_only,
3125
repository=repository)
2076
3127
# 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
3128
result = self._custom_format.initialize(a_controldir, name=name,
3129
append_revisions_only=append_revisions_only,
3130
repository=repository)
3131
if (isinstance(a_controldir, RemoteBzrDir) and
2079
3132
not isinstance(result, RemoteBranch)):
2080
result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result,
3133
result = RemoteBranch(a_controldir, a_controldir.find_repository(), result,
2084
def initialize(self, a_bzrdir, name=None):
3137
def initialize(self, a_controldir, name=None, repository=None,
3138
append_revisions_only=None):
3140
name = a_controldir._get_selected_branch()
2085
3141
# 1) get the network name to use.
2086
3142
if self._custom_format:
2087
3143
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')()
3145
# Select the current breezy default and ask for that.
3146
reference_bzrdir_format = controldir.format_registry.get('default')()
2091
3147
reference_format = reference_bzrdir_format.get_branch_format()
2092
3148
self._custom_format = reference_format
2093
3149
network_name = reference_format.network_name()
2094
3150
# 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
3151
if not isinstance(a_controldir, RemoteBzrDir):
3152
return self._vfs_initialize(a_controldir, name=name,
3153
append_revisions_only=append_revisions_only,
3154
repository=repository)
3155
medium = a_controldir._client._medium
2098
3156
if medium._is_remote_before((1, 13)):
2099
return self._vfs_initialize(a_bzrdir, name=name)
3157
return self._vfs_initialize(a_controldir, name=name,
3158
append_revisions_only=append_revisions_only,
3159
repository=repository)
2100
3160
# Creating on a remote bzr dir.
2101
3161
# 2) try direct creation via RPC
2102
path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
2103
if name is not None:
3162
path = a_controldir._path_for_remote_call(a_controldir._client)
2104
3164
# XXX JRV20100304: Support creating colocated branches
2105
3165
raise errors.NoColocatedBranchSupport(self)
2106
3166
verb = 'BzrDir.create_branch'
2108
response = a_bzrdir._call(verb, path, network_name)
3168
response = a_controldir._call(verb, path, network_name)
2109
3169
except errors.UnknownSmartMethod:
2110
3170
# Fallback - use vfs methods
2111
3171
medium._remember_remote_is_before((1, 13))
2112
return self._vfs_initialize(a_bzrdir, name=name)
3172
return self._vfs_initialize(a_controldir, name=name,
3173
append_revisions_only=append_revisions_only,
3174
repository=repository)
2113
3175
if response[0] != 'ok':
2114
3176
raise errors.UnexpectedSmartServerResponse(response)
2115
3177
# Turn the response into a RemoteRepository object.
2116
3178
format = RemoteBranchFormat(network_name=response[1])
2117
3179
repo_format = response_tuple_to_repo_format(response[3:])
2118
if response[2] == '':
2119
repo_bzrdir = a_bzrdir
3180
repo_path = response[2]
3181
if repository is not None:
3182
remote_repo_url = urlutils.join(a_controldir.user_url, repo_path)
3183
url_diff = urlutils.relative_url(repository.user_url,
3186
raise AssertionError(
3187
'repository.user_url %r does not match URL from server '
3188
'response (%r + %r)'
3189
% (repository.user_url, a_controldir.user_url, repo_path))
3190
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,
3193
repo_bzrdir = a_controldir
3195
repo_bzrdir = RemoteBzrDir(
3196
a_controldir.root_transport.clone(repo_path), a_controldir._format,
3197
a_controldir._client)
3198
remote_repo = RemoteRepository(repo_bzrdir, repo_format)
3199
remote_branch = RemoteBranch(a_controldir, remote_repo,
2126
3200
format=format, setup_stacking=False, name=name)
3201
if append_revisions_only:
3202
remote_branch.set_append_revisions_only(append_revisions_only)
2127
3203
# XXX: We know this is a new branch, so it must have revno 0, revid
2128
3204
# NULL_REVISION. Creating the branch locked would make this be unable
2129
3205
# to be wrong; here its simply very unlikely to be wrong. RBC 20090225
2148
3224
self._ensure_real()
2149
3225
return self._custom_format.supports_set_append_revisions_only()
3227
def _use_default_local_heads_to_fetch(self):
3228
# If the branch format is a metadir format *and* its heads_to_fetch
3229
# implementation is not overridden vs the base class, we can use the
3230
# base class logic rather than use the heads_to_fetch RPC. This is
3231
# usually cheaper in terms of net round trips, as the last-revision and
3232
# tags info fetched is cached and would be fetched anyway.
3234
if isinstance(self._custom_format, bzrbranch.BranchFormatMetadir):
3235
branch_class = self._custom_format._branch_class()
3236
heads_to_fetch_impl = branch_class.heads_to_fetch.__func__
3237
if heads_to_fetch_impl is branch.Branch.heads_to_fetch.__func__:
3242
class RemoteBranchStore(_mod_config.IniFileStore):
3243
"""Branch store which attempts to use HPSS calls to retrieve branch store.
3245
Note that this is specific to bzr-based formats.
3248
def __init__(self, branch):
3249
super(RemoteBranchStore, self).__init__()
3250
self.branch = branch
3252
self._real_store = None
3254
def external_url(self):
3255
return urlutils.join(self.branch.user_url, 'branch.conf')
3257
def _load_content(self):
3258
path = self.branch._remote_path()
3260
response, handler = self.branch._call_expecting_body(
3261
'Branch.get_config_file', path)
3262
except errors.UnknownSmartMethod:
3264
return self._real_store._load_content()
3265
if len(response) and response[0] != 'ok':
3266
raise errors.UnexpectedSmartServerResponse(response)
3267
return handler.read_body_bytes()
3269
def _save_content(self, content):
3270
path = self.branch._remote_path()
3272
response, handler = self.branch._call_with_body_bytes_expecting_body(
3273
'Branch.put_config_file', (path,
3274
self.branch._lock_token, self.branch._repo_lock_token),
3276
except errors.UnknownSmartMethod:
3278
return self._real_store._save_content(content)
3279
handler.cancel_read_body()
3280
if response != ('ok', ):
3281
raise errors.UnexpectedSmartServerResponse(response)
3283
def _ensure_real(self):
3284
self.branch._ensure_real()
3285
if self._real_store is None:
3286
self._real_store = _mod_config.BranchStore(self.branch)
2152
3289
class RemoteBranch(branch.Branch, _RpcHelper, lock._RelockDebugMixin):
2153
3290
"""Branch stored on a server accessed by HPSS RPC.
2654
3840
_override_hook_target=self, **kwargs)
2656
3842
@needs_read_lock
2657
def push(self, target, overwrite=False, stop_revision=None):
3843
def push(self, target, overwrite=False, stop_revision=None, lossy=False):
2658
3844
self._ensure_real()
2659
3845
return self._real_branch.push(
2660
target, overwrite=overwrite, stop_revision=stop_revision,
3846
target, overwrite=overwrite, stop_revision=stop_revision, lossy=lossy,
2661
3847
_override_hook_source_branch=self)
3849
def peek_lock_mode(self):
3850
return self._lock_mode
2663
3852
def is_locked(self):
2664
3853
return self._lock_count >= 1
2666
3855
@needs_read_lock
3856
def revision_id_to_dotted_revno(self, revision_id):
3857
"""Given a revision id, return its dotted revno.
3859
:return: a tuple like (1,) or (400,1,3).
3862
response = self._call('Branch.revision_id_to_revno',
3863
self._remote_path(), revision_id)
3864
except errors.UnknownSmartMethod:
3866
return self._real_branch.revision_id_to_dotted_revno(revision_id)
3867
if response[0] == 'ok':
3868
return tuple([int(x) for x in response[1:]])
3870
raise errors.UnexpectedSmartServerResponse(response)
2667
3873
def revision_id_to_revno(self, revision_id):
2669
return self._real_branch.revision_id_to_revno(revision_id)
3874
"""Given a revision id on the branch mainline, return its revno.
3879
response = self._call('Branch.revision_id_to_revno',
3880
self._remote_path(), revision_id)
3881
except errors.UnknownSmartMethod:
3883
return self._real_branch.revision_id_to_revno(revision_id)
3884
if response[0] == 'ok':
3885
if len(response) == 2:
3886
return int(response[1])
3887
raise NoSuchRevision(self, revision_id)
3889
raise errors.UnexpectedSmartServerResponse(response)
2671
3891
@needs_write_lock
2672
3892
def set_last_revision_info(self, revno, revision_id):
2673
3893
# XXX: These should be returned by the set_last_revision_info verb
2674
3894
old_revno, old_revid = self.last_revision_info()
2675
3895
self._run_pre_change_branch_tip_hooks(revno, revision_id)
2676
revision_id = ensure_null(revision_id)
3896
if not revision_id or not isinstance(revision_id, basestring):
3897
raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2678
3899
response = self._call('Branch.set_last_revision_info',
2679
3900
self._remote_path(), self._lock_token, self._repo_lock_token,
2708
3929
except errors.UnknownSmartMethod:
2709
3930
medium._remember_remote_is_before((1, 6))
2710
3931
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))
3932
graph = self.repository.get_graph()
3933
(last_revno, last_revid) = self.last_revision_info()
3934
known_revision_ids = [
3935
(last_revid, last_revno),
3936
(_mod_revision.NULL_REVISION, 0),
3938
if last_rev is not None:
3939
if not graph.is_ancestor(last_rev, revision_id):
3940
# our previous tip is not merged into stop_revision
3941
raise errors.DivergedBranches(self, other_branch)
3942
revno = graph.find_distance_to_null(revision_id, known_revision_ids)
3943
self.set_last_revision_info(revno, revision_id)
2714
3945
def set_push_location(self, location):
3946
self._set_config_location('push_location', location)
3948
def heads_to_fetch(self):
3949
if self._format._use_default_local_heads_to_fetch():
3950
# We recognise this format, and its heads-to-fetch implementation
3951
# is the default one (tip + tags). In this case it's cheaper to
3952
# just use the default implementation rather than a special RPC as
3953
# the tip and tags data is cached.
3954
return branch.Branch.heads_to_fetch(self)
3955
medium = self._client._medium
3956
if medium._is_remote_before((2, 4)):
3957
return self._vfs_heads_to_fetch()
3959
return self._rpc_heads_to_fetch()
3960
except errors.UnknownSmartMethod:
3961
medium._remember_remote_is_before((2, 4))
3962
return self._vfs_heads_to_fetch()
3964
def _rpc_heads_to_fetch(self):
3965
response = self._call('Branch.heads_to_fetch', self._remote_path())
3966
if len(response) != 2:
3967
raise errors.UnexpectedSmartServerResponse(response)
3968
must_fetch, if_present_fetch = response
3969
return set(must_fetch), set(if_present_fetch)
3971
def _vfs_heads_to_fetch(self):
2715
3972
self._ensure_real()
2716
return self._real_branch.set_push_location(location)
3973
return self._real_branch.heads_to_fetch()
2719
3976
class RemoteConfig(object):
2774
4041
medium = self._branch._client._medium
2775
4042
if medium._is_remote_before((1, 14)):
2776
4043
return self._vfs_set_option(value, name, section)
4044
if isinstance(value, dict):
4045
if medium._is_remote_before((2, 2)):
4046
return self._vfs_set_option(value, name, section)
4047
return self._set_config_option_dict(value, name, section)
4049
return self._set_config_option(value, name, section)
4051
def _set_config_option(self, value, name, section):
2778
4053
path = self._branch._remote_path()
2779
4054
response = self._branch._client.call('Branch.set_config_option',
2780
4055
path, self._branch._lock_token, self._branch._repo_lock_token,
2781
4056
value.encode('utf8'), name, section or '')
2782
4057
except errors.UnknownSmartMethod:
4058
medium = self._branch._client._medium
2783
4059
medium._remember_remote_is_before((1, 14))
2784
4060
return self._vfs_set_option(value, name, section)
2785
4061
if response != ():
2786
4062
raise errors.UnexpectedSmartServerResponse(response)
4064
def _serialize_option_dict(self, option_dict):
4066
for key, value in option_dict.items():
4067
if isinstance(key, unicode):
4068
key = key.encode('utf8')
4069
if isinstance(value, unicode):
4070
value = value.encode('utf8')
4071
utf8_dict[key] = value
4072
return bencode.bencode(utf8_dict)
4074
def _set_config_option_dict(self, value, name, section):
4076
path = self._branch._remote_path()
4077
serialised_dict = self._serialize_option_dict(value)
4078
response = self._branch._client.call(
4079
'Branch.set_config_option_dict',
4080
path, self._branch._lock_token, self._branch._repo_lock_token,
4081
serialised_dict, name, section or '')
4082
except errors.UnknownSmartMethod:
4083
medium = self._branch._client._medium
4084
medium._remember_remote_is_before((2, 2))
4085
return self._vfs_set_option(value, name, section)
4087
raise errors.UnexpectedSmartServerResponse(response)
2788
4089
def _real_object(self):
2789
4090
self._branch._ensure_real()
2790
4091
return self._branch._real_branch
2867
4171
return context['path']
2868
except KeyError, key_err:
4172
except KeyError as key_err:
2870
4174
return err.error_args[0]
2871
except IndexError, idx_err:
4175
except IndexError as idx_err:
2873
4177
'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'):
4181
translator = error_translators.get(err.error_verb)
4185
raise translator(err, find, get_path)
4187
translator = no_context_error_translators.get(err.error_verb)
4189
raise errors.UnknownErrorFromSmartServer(err)
4191
raise translator(err)
4194
error_translators.register('NoSuchRevision',
4195
lambda err, find, get_path: NoSuchRevision(
4196
find('branch'), err.error_args[0]))
4197
error_translators.register('nosuchrevision',
4198
lambda err, find, get_path: NoSuchRevision(
4199
find('repository'), err.error_args[0]))
4201
def _translate_nobranch_error(err, find, get_path):
4202
if len(err.error_args) >= 1:
4203
extra = err.error_args[0]
4206
return errors.NotBranchError(path=find('bzrdir').root_transport.base,
4209
error_translators.register('nobranch', _translate_nobranch_error)
4210
error_translators.register('norepository',
4211
lambda err, find, get_path: errors.NoRepositoryPresent(
4213
error_translators.register('UnlockableTransport',
4214
lambda err, find, get_path: errors.UnlockableTransport(
4215
find('bzrdir').root_transport))
4216
error_translators.register('TokenMismatch',
4217
lambda err, find, get_path: errors.TokenMismatch(
4218
find('token'), '(remote token)'))
4219
error_translators.register('Diverged',
4220
lambda err, find, get_path: errors.DivergedBranches(
4221
find('branch'), find('other_branch')))
4222
error_translators.register('NotStacked',
4223
lambda err, find, get_path: errors.NotStacked(branch=find('branch')))
4225
def _translate_PermissionDenied(err, find, get_path):
4227
if len(err.error_args) >= 2:
4228
extra = err.error_args[1]
4231
return errors.PermissionDenied(path, extra=extra)
4233
error_translators.register('PermissionDenied', _translate_PermissionDenied)
4234
error_translators.register('ReadError',
4235
lambda err, find, get_path: errors.ReadError(get_path()))
4236
error_translators.register('NoSuchFile',
4237
lambda err, find, get_path: errors.NoSuchFile(get_path()))
4238
error_translators.register('TokenLockingNotSupported',
4239
lambda err, find, get_path: errors.TokenLockingNotSupported(
4240
find('repository')))
4241
error_translators.register('UnsuspendableWriteGroup',
4242
lambda err, find, get_path: errors.UnsuspendableWriteGroup(
4243
repository=find('repository')))
4244
error_translators.register('UnresumableWriteGroup',
4245
lambda err, find, get_path: errors.UnresumableWriteGroup(
4246
repository=find('repository'), write_groups=err.error_args[0],
4247
reason=err.error_args[1]))
4248
no_context_error_translators.register('IncompatibleRepositories',
4249
lambda err: errors.IncompatibleRepositories(
4250
err.error_args[0], err.error_args[1], err.error_args[2]))
4251
no_context_error_translators.register('LockContention',
4252
lambda err: errors.LockContention('(remote lock)'))
4253
no_context_error_translators.register('LockFailed',
4254
lambda err: errors.LockFailed(err.error_args[0], err.error_args[1]))
4255
no_context_error_translators.register('TipChangeRejected',
4256
lambda err: errors.TipChangeRejected(err.error_args[0].decode('utf8')))
4257
no_context_error_translators.register('UnstackableBranchFormat',
4258
lambda err: errors.UnstackableBranchFormat(*err.error_args))
4259
no_context_error_translators.register('UnstackableRepositoryFormat',
4260
lambda err: errors.UnstackableRepositoryFormat(*err.error_args))
4261
no_context_error_translators.register('FileExists',
4262
lambda err: errors.FileExists(err.error_args[0]))
4263
no_context_error_translators.register('DirectoryNotEmpty',
4264
lambda err: errors.DirectoryNotEmpty(err.error_args[0]))
4266
def _translate_short_readv_error(err):
4267
args = err.error_args
4268
return errors.ShortReadvError(args[0], int(args[1]), int(args[2]),
4271
no_context_error_translators.register('ShortReadvError',
4272
_translate_short_readv_error)
4274
def _translate_unicode_error(err):
2932
4275
encoding = str(err.error_args[0]) # encoding must always be a string
2933
4276
val = err.error_args[1]
2934
4277
start = int(err.error_args[2])