89
# Note: RemoteBzrDirFormat is in bzrdir.py
91
class RemoteBzrDir(BzrDir, _RpcHelper):
111
# Note that RemoteBzrDirProber lives in breezy.bzrdir so breezy.remote
112
# does not have to be imported unless a remote format is involved.
114
class RemoteBzrDirFormat(_mod_bzrdir.BzrDirMetaFormat1):
115
"""Format representing bzrdirs accessed via a smart server"""
117
supports_workingtrees = False
119
colocated_branches = False
122
_mod_bzrdir.BzrDirMetaFormat1.__init__(self)
123
# XXX: It's a bit ugly that the network name is here, because we'd
124
# like to believe that format objects are stateless or at least
125
# immutable, However, we do at least avoid mutating the name after
126
# it's returned. See <https://bugs.launchpad.net/bzr/+bug/504102>
127
self._network_name = None
130
return "%s(_network_name=%r)" % (self.__class__.__name__,
133
def get_format_description(self):
134
if self._network_name:
136
real_format = controldir.network_format_registry.get(
141
return 'Remote: ' + real_format.get_format_description()
142
return 'bzr remote bzrdir'
144
def get_format_string(self):
145
raise NotImplementedError(self.get_format_string)
147
def network_name(self):
148
if self._network_name:
149
return self._network_name
151
raise AssertionError("No network name set.")
153
def initialize_on_transport(self, transport):
155
# hand off the request to the smart server
156
client_medium = transport.get_smart_medium()
157
except errors.NoSmartMedium:
158
# TODO: lookup the local format from a server hint.
159
local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
160
return local_dir_format.initialize_on_transport(transport)
161
client = _SmartClient(client_medium)
162
path = client.remote_path_from_transport(transport)
164
response = client.call('BzrDirFormat.initialize', path)
165
except errors.ErrorFromSmartServer as err:
166
_translate_error(err, path=path)
167
if response[0] != 'ok':
168
raise errors.SmartProtocolError('unexpected response code %s' % (response,))
169
format = RemoteBzrDirFormat()
170
self._supply_sub_formats_to(format)
171
return RemoteBzrDir(transport, format)
173
def parse_NoneTrueFalse(self, arg):
180
raise AssertionError("invalid arg %r" % arg)
182
def _serialize_NoneTrueFalse(self, arg):
189
def _serialize_NoneString(self, arg):
192
def initialize_on_transport_ex(self, transport, use_existing_dir=False,
193
create_prefix=False, force_new_repo=False, stacked_on=None,
194
stack_on_pwd=None, repo_format_name=None, make_working_trees=None,
197
# hand off the request to the smart server
198
client_medium = transport.get_smart_medium()
199
except errors.NoSmartMedium:
202
# Decline to open it if the server doesn't support our required
203
# version (3) so that the VFS-based transport will do it.
204
if client_medium.should_probe():
206
server_version = client_medium.protocol_version()
207
if server_version != '2':
211
except errors.SmartProtocolError:
212
# Apparently there's no usable smart server there, even though
213
# the medium supports the smart protocol.
218
client = _SmartClient(client_medium)
219
path = client.remote_path_from_transport(transport)
220
if client_medium._is_remote_before((1, 16)):
223
# TODO: lookup the local format from a server hint.
224
local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
225
self._supply_sub_formats_to(local_dir_format)
226
return local_dir_format.initialize_on_transport_ex(transport,
227
use_existing_dir=use_existing_dir, create_prefix=create_prefix,
228
force_new_repo=force_new_repo, stacked_on=stacked_on,
229
stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
230
make_working_trees=make_working_trees, shared_repo=shared_repo,
232
return self._initialize_on_transport_ex_rpc(client, path, transport,
233
use_existing_dir, create_prefix, force_new_repo, stacked_on,
234
stack_on_pwd, repo_format_name, make_working_trees, shared_repo)
236
def _initialize_on_transport_ex_rpc(self, 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
args.append(self._serialize_NoneTrueFalse(use_existing_dir))
241
args.append(self._serialize_NoneTrueFalse(create_prefix))
242
args.append(self._serialize_NoneTrueFalse(force_new_repo))
243
args.append(self._serialize_NoneString(stacked_on))
244
# stack_on_pwd is often/usually our transport
247
stack_on_pwd = transport.relpath(stack_on_pwd)
250
except errors.PathNotChild:
252
args.append(self._serialize_NoneString(stack_on_pwd))
253
args.append(self._serialize_NoneString(repo_format_name))
254
args.append(self._serialize_NoneTrueFalse(make_working_trees))
255
args.append(self._serialize_NoneTrueFalse(shared_repo))
256
request_network_name = self._network_name or \
257
_mod_bzrdir.BzrDirFormat.get_default_format().network_name()
259
response = client.call('BzrDirFormat.initialize_ex_1.16',
260
request_network_name, path, *args)
261
except errors.UnknownSmartMethod:
262
client._medium._remember_remote_is_before((1,16))
263
local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
264
self._supply_sub_formats_to(local_dir_format)
265
return local_dir_format.initialize_on_transport_ex(transport,
266
use_existing_dir=use_existing_dir, create_prefix=create_prefix,
267
force_new_repo=force_new_repo, stacked_on=stacked_on,
268
stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
269
make_working_trees=make_working_trees, shared_repo=shared_repo,
271
except errors.ErrorFromSmartServer as err:
272
_translate_error(err, path=path)
273
repo_path = response[0]
274
bzrdir_name = response[6]
275
require_stacking = response[7]
276
require_stacking = self.parse_NoneTrueFalse(require_stacking)
277
format = RemoteBzrDirFormat()
278
format._network_name = bzrdir_name
279
self._supply_sub_formats_to(format)
280
bzrdir = RemoteBzrDir(transport, format, _client=client)
282
repo_format = response_tuple_to_repo_format(response[1:])
286
repo_bzrdir_format = RemoteBzrDirFormat()
287
repo_bzrdir_format._network_name = response[5]
288
repo_bzr = RemoteBzrDir(transport.clone(repo_path),
292
final_stack = response[8] or None
293
final_stack_pwd = response[9] or None
295
final_stack_pwd = urlutils.join(
296
transport.base, final_stack_pwd)
297
remote_repo = RemoteRepository(repo_bzr, repo_format)
298
if len(response) > 10:
299
# Updated server verb that locks remotely.
300
repo_lock_token = response[10] or None
301
remote_repo.lock_write(repo_lock_token, _skip_rpc=True)
303
remote_repo.dont_leave_lock_in_place()
305
remote_repo.lock_write()
306
policy = _mod_bzrdir.UseExistingRepository(remote_repo, final_stack,
307
final_stack_pwd, require_stacking)
308
policy.acquire_repository()
312
bzrdir._format.set_branch_format(self.get_branch_format())
314
# The repo has already been created, but we need to make sure that
315
# we'll make a stackable branch.
316
bzrdir._format.require_stacking(_skip_repo=True)
317
return remote_repo, bzrdir, require_stacking, policy
319
def _open(self, transport):
320
return RemoteBzrDir(transport, self)
322
def __eq__(self, other):
323
if not isinstance(other, RemoteBzrDirFormat):
325
return self.get_format_description() == other.get_format_description()
327
def __return_repository_format(self):
328
# Always return a RemoteRepositoryFormat object, but if a specific bzr
329
# repository format has been asked for, tell the RemoteRepositoryFormat
330
# that it should use that for init() etc.
331
result = RemoteRepositoryFormat()
332
custom_format = getattr(self, '_repository_format', None)
334
if isinstance(custom_format, RemoteRepositoryFormat):
337
# We will use the custom format to create repositories over the
338
# wire; expose its details like rich_root_data for code to
340
result._custom_format = custom_format
343
def get_branch_format(self):
344
result = _mod_bzrdir.BzrDirMetaFormat1.get_branch_format(self)
345
if not isinstance(result, RemoteBranchFormat):
346
new_result = RemoteBranchFormat()
347
new_result._custom_format = result
349
self.set_branch_format(new_result)
353
repository_format = property(__return_repository_format,
354
_mod_bzrdir.BzrDirMetaFormat1._set_repository_format) #.im_func)
357
class RemoteControlStore(_mod_config.IniFileStore):
358
"""Control store which attempts to use HPSS calls to retrieve control store.
360
Note that this is specific to bzr-based formats.
363
def __init__(self, bzrdir):
364
super(RemoteControlStore, self).__init__()
366
self._real_store = None
368
def lock_write(self, token=None):
370
return self._real_store.lock_write(token)
374
return self._real_store.unlock()
378
# We need to be able to override the undecorated implementation
379
self.save_without_locking()
381
def save_without_locking(self):
382
super(RemoteControlStore, self).save()
384
def _ensure_real(self):
385
self.bzrdir._ensure_real()
386
if self._real_store is None:
387
self._real_store = _mod_config.ControlStore(self.bzrdir)
389
def external_url(self):
390
return urlutils.join(self.branch.user_url, 'control.conf')
392
def _load_content(self):
393
medium = self.bzrdir._client._medium
394
path = self.bzrdir._path_for_remote_call(self.bzrdir._client)
396
response, handler = self.bzrdir._call_expecting_body(
397
'BzrDir.get_config_file', path)
398
except errors.UnknownSmartMethod:
400
return self._real_store._load_content()
401
if len(response) and response[0] != 'ok':
402
raise errors.UnexpectedSmartServerResponse(response)
403
return handler.read_body_bytes()
405
def _save_content(self, content):
406
# FIXME JRV 2011-11-22: Ideally this should use a
407
# HPSS call too, but at the moment it is not possible
408
# to write lock control directories.
410
return self._real_store._save_content(content)
413
class RemoteBzrDir(_mod_bzrdir.BzrDir, _RpcHelper):
92
414
"""Control directory on a remote server, accessed via bzr:// or similar."""
94
416
def __init__(self, transport, format, _client=None, _force_probe=False):
266
659
def destroy_branch(self, name=None):
267
660
"""See BzrDir.destroy_branch"""
269
self._real_bzrdir.destroy_branch(name=name)
662
name = self._get_selected_branch()
664
raise errors.NoColocatedBranchSupport(self)
665
path = self._path_for_remote_call(self._client)
671
response = self._call('BzrDir.destroy_branch', path, *args)
672
except errors.UnknownSmartMethod:
674
self._real_bzrdir.destroy_branch(name=name)
675
self._next_open_branch_result = None
270
677
self._next_open_branch_result = None
678
if response[0] != 'ok':
679
raise SmartProtocolError('unexpected response code %s' % (response,))
272
def create_workingtree(self, revision_id=None, from_branch=None):
681
def create_workingtree(self, revision_id=None, from_branch=None,
682
accelerator_tree=None, hardlink=False):
273
683
raise errors.NotLocalUrl(self.transport.base)
275
def find_branch_format(self):
685
def find_branch_format(self, name=None):
276
686
"""Find the branch 'format' for this bzrdir.
278
688
This might be a synthetic object for e.g. RemoteBranch and SVN.
280
b = self.open_branch()
690
b = self.open_branch(name=name)
283
def get_branch_reference(self):
693
def get_branches(self, possible_transports=None, ignore_fallbacks=False):
694
path = self._path_for_remote_call(self._client)
696
response, handler = self._call_expecting_body(
697
'BzrDir.get_branches', path)
698
except errors.UnknownSmartMethod:
700
return self._real_bzrdir.get_branches()
701
if response[0] != "success":
702
raise errors.UnexpectedSmartServerResponse(response)
703
body = bencode.bdecode(handler.read_body_bytes())
705
for name, value in viewitems(body):
706
ret[name] = self._open_branch(name, value[0], value[1],
707
possible_transports=possible_transports,
708
ignore_fallbacks=ignore_fallbacks)
711
def set_branch_reference(self, target_branch, name=None):
712
"""See BzrDir.set_branch_reference()."""
714
name = self._get_selected_branch()
716
raise errors.NoColocatedBranchSupport(self)
718
return self._real_bzrdir.set_branch_reference(target_branch, name=name)
720
def get_branch_reference(self, name=None):
284
721
"""See BzrDir.get_branch_reference()."""
723
name = self._get_selected_branch()
725
raise errors.NoColocatedBranchSupport(self)
285
726
response = self._get_branch_reference()
286
727
if response[0] == 'ref':
287
728
return response[1]
318
759
raise errors.UnexpectedSmartServerResponse(response)
321
def _get_tree_branch(self):
762
def _get_tree_branch(self, name=None):
322
763
"""See BzrDir._get_tree_branch()."""
323
return None, self.open_branch()
764
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':
766
def _open_branch(self, name, kind, location_or_format,
767
ignore_fallbacks=False, possible_transports=None):
336
769
# a branch reference, use the existing BranchReference logic.
337
770
format = BranchReferenceFormat()
338
771
return format.open(self, name=name, _found=True,
339
location=response[1], ignore_fallbacks=ignore_fallbacks)
340
branch_format_name = response[1]
772
location=location_or_format, ignore_fallbacks=ignore_fallbacks,
773
possible_transports=possible_transports)
774
branch_format_name = location_or_format
341
775
if not branch_format_name:
342
776
branch_format_name = None
343
777
format = RemoteBranchFormat(network_name=branch_format_name)
344
778
return RemoteBranch(self, self.find_repository(), format=format,
345
setup_stacking=not ignore_fallbacks, name=name)
779
setup_stacking=not ignore_fallbacks, name=name,
780
possible_transports=possible_transports)
782
def open_branch(self, name=None, unsupported=False,
783
ignore_fallbacks=False, possible_transports=None):
785
name = self._get_selected_branch()
787
raise errors.NoColocatedBranchSupport(self)
789
raise NotImplementedError('unsupported flag support not implemented yet.')
790
if self._next_open_branch_result is not None:
791
# See create_branch for details.
792
result = self._next_open_branch_result
793
self._next_open_branch_result = None
795
response = self._get_branch_reference()
796
return self._open_branch(name, response[0], response[1],
797
possible_transports=possible_transports,
798
ignore_fallbacks=ignore_fallbacks)
347
800
def _open_repo_v1(self, path):
348
801
verb = 'BzrDir.find_repository'
1195
1771
raise errors.UnexpectedSmartServerResponse(response)
1197
1774
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,
1775
"""Create a descendent repository for new development.
1777
Unlike clone, this does not copy the settings of the repository.
1779
dest_repo = self._create_sprouting_repo(to_bzrdir, shared=False)
1202
1780
dest_repo.fetch(self, revision_id=revision_id)
1203
1781
return dest_repo
1783
def _create_sprouting_repo(self, a_bzrdir, shared):
1784
if not isinstance(a_bzrdir._format, self.bzrdir._format.__class__):
1785
# use target default format.
1786
dest_repo = a_bzrdir.create_repository()
1788
# Most control formats need the repository to be specifically
1789
# created, but on some old all-in-one formats it's not needed
1791
dest_repo = self._format.initialize(a_bzrdir, shared=shared)
1792
except errors.UninitializableFormat:
1793
dest_repo = a_bzrdir.open_repository()
1205
1796
### These methods are just thin shims to the VFS object for now.
1207
1799
def revision_tree(self, revision_id):
1209
return self._real_repository.revision_tree(revision_id)
1800
revision_id = _mod_revision.ensure_null(revision_id)
1801
if revision_id == _mod_revision.NULL_REVISION:
1802
return InventoryRevisionTree(self,
1803
Inventory(root_id=None), _mod_revision.NULL_REVISION)
1805
return list(self.revision_trees([revision_id]))[0]
1211
1807
def get_serializer_format(self):
1213
return self._real_repository.get_serializer_format()
1808
path = self.bzrdir._path_for_remote_call(self._client)
1810
response = self._call('VersionedFileRepository.get_serializer_format',
1812
except errors.UnknownSmartMethod:
1814
return self._real_repository.get_serializer_format()
1815
if response[0] != 'ok':
1816
raise errors.UnexpectedSmartServerResponse(response)
1215
1819
def get_commit_builder(self, branch, parents, config, timestamp=None,
1216
1820
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)
1821
revision_id=None, lossy=False):
1822
"""Obtain a CommitBuilder for this repository.
1824
:param branch: Branch to commit to.
1825
:param parents: Revision ids of the parents of the new revision.
1826
:param config: Configuration to use.
1827
:param timestamp: Optional timestamp recorded for commit.
1828
:param timezone: Optional timezone for timestamp.
1829
:param committer: Optional committer to set for commit.
1830
:param revprops: Optional dictionary of revision properties.
1831
:param revision_id: Optional revision id.
1832
:param lossy: Whether to discard data that can not be natively
1833
represented, when pushing to a foreign VCS
1835
if self._fallback_repositories and not self._format.supports_chks:
1836
raise errors.BzrError("Cannot commit directly to a stacked branch"
1837
" in pre-2a formats. See "
1838
"https://bugs.launchpad.net/bzr/+bug/375013 for details.")
1839
if self._format.rich_root_data:
1840
commit_builder_kls = vf_repository.VersionedFileRootCommitBuilder
1842
commit_builder_kls = vf_repository.VersionedFileCommitBuilder
1843
result = commit_builder_kls(self, parents, config,
1844
timestamp, timezone, committer, revprops, revision_id,
1846
self.start_write_group()
1227
1849
def add_fallback_repository(self, repository):
1228
1850
"""Add a repository to use for looking up data not held locally.
1272
1895
delta, new_revision_id, parents, basis_inv=basis_inv,
1273
1896
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)
1898
def add_revision(self, revision_id, rev, inv=None):
1899
_mod_revision.check_not_reserved_id(revision_id)
1900
key = (revision_id,)
1901
# check inventory present
1902
if not self.inventories.get_parent_map([key]):
1904
raise errors.WeaveRevisionNotPresent(revision_id,
1907
# yes, this is not suitable for adding with ghosts.
1908
rev.inventory_sha1 = self.add_inventory(revision_id, inv,
1911
rev.inventory_sha1 = self.inventories.get_sha1s([key])[key]
1912
self._add_revision(rev)
1914
def _add_revision(self, rev):
1915
if self._real_repository is not None:
1916
return self._real_repository._add_revision(rev)
1917
text = self._serializer.write_revision_to_string(rev)
1918
key = (rev.revision_id,)
1919
parents = tuple((parent,) for parent in rev.parent_ids)
1920
self._write_group_tokens, missing_keys = self._get_sink().insert_stream(
1921
[('revisions', [FulltextContentFactory(key, parents, None, text)])],
1922
self._format, self._write_group_tokens)
1280
1924
@needs_read_lock
1281
1925
def get_inventory(self, revision_id):
1926
return list(self.iter_inventories([revision_id]))[0]
1928
def _iter_inventories_rpc(self, revision_ids, ordering):
1929
if ordering is None:
1930
ordering = 'unordered'
1931
path = self.bzrdir._path_for_remote_call(self._client)
1932
body = "\n".join(revision_ids)
1933
response_tuple, response_handler = (
1934
self._call_with_body_bytes_expecting_body(
1935
"VersionedFileRepository.get_inventories",
1936
(path, ordering), body))
1937
if response_tuple[0] != "ok":
1938
raise errors.UnexpectedSmartServerResponse(response_tuple)
1939
deserializer = inventory_delta.InventoryDeltaDeserializer()
1940
byte_stream = response_handler.read_streamed_body()
1941
decoded = smart_repo._byte_stream_to_stream(byte_stream)
1943
# no results whatsoever
1945
src_format, stream = decoded
1946
if src_format.network_name() != self._format.network_name():
1947
raise AssertionError(
1948
"Mismatched RemoteRepository and stream src %r, %r" % (
1949
src_format.network_name(), self._format.network_name()))
1950
# ignore the src format, it's not really relevant
1951
prev_inv = Inventory(root_id=None,
1952
revision_id=_mod_revision.NULL_REVISION)
1953
# there should be just one substream, with inventory deltas
1954
substream_kind, substream = next(stream)
1955
if substream_kind != "inventory-deltas":
1956
raise AssertionError(
1957
"Unexpected stream %r received" % substream_kind)
1958
for record in substream:
1959
(parent_id, new_id, versioned_root, tree_references, invdelta) = (
1960
deserializer.parse_text_bytes(record.get_bytes_as("fulltext")))
1961
if parent_id != prev_inv.revision_id:
1962
raise AssertionError("invalid base %r != %r" % (parent_id,
1963
prev_inv.revision_id))
1964
inv = prev_inv.create_by_apply_delta(invdelta, new_id)
1965
yield inv, inv.revision_id
1968
def _iter_inventories_vfs(self, revision_ids, ordering=None):
1282
1969
self._ensure_real()
1283
return self._real_repository.get_inventory(revision_id)
1970
return self._real_repository._iter_inventories(revision_ids, ordering)
1285
1972
def iter_inventories(self, revision_ids, ordering=None):
1287
return self._real_repository.iter_inventories(revision_ids, ordering)
1973
"""Get many inventories by revision_ids.
1975
This will buffer some or all of the texts used in constructing the
1976
inventories in memory, but will only parse a single inventory at a
1979
:param revision_ids: The expected revision ids of the inventories.
1980
:param ordering: optional ordering, e.g. 'topological'. If not
1981
specified, the order of revision_ids will be preserved (by
1982
buffering if necessary).
1983
:return: An iterator of inventories.
1985
if ((None in revision_ids)
1986
or (_mod_revision.NULL_REVISION in revision_ids)):
1987
raise ValueError('cannot get null revision inventory')
1988
for inv, revid in self._iter_inventories(revision_ids, ordering):
1990
raise errors.NoSuchRevision(self, revid)
1993
def _iter_inventories(self, revision_ids, ordering=None):
1994
if len(revision_ids) == 0:
1996
missing = set(revision_ids)
1997
if ordering is None:
1998
order_as_requested = True
2000
order = list(revision_ids)
2002
next_revid = order.pop()
2004
order_as_requested = False
2005
if ordering != 'unordered' and self._fallback_repositories:
2006
raise ValueError('unsupported ordering %r' % ordering)
2007
iter_inv_fns = [self._iter_inventories_rpc] + [
2008
fallback._iter_inventories for fallback in
2009
self._fallback_repositories]
2011
for iter_inv in iter_inv_fns:
2012
request = [revid for revid in revision_ids if revid in missing]
2013
for inv, revid in iter_inv(request, ordering):
2016
missing.remove(inv.revision_id)
2017
if ordering != 'unordered':
2021
if order_as_requested:
2022
# Yield as many results as we can while preserving order.
2023
while next_revid in invs:
2024
inv = invs.pop(next_revid)
2025
yield inv, inv.revision_id
2027
next_revid = order.pop()
2029
# We still want to fully consume the stream, just
2030
# in case it is not actually finished at this point
2033
except errors.UnknownSmartMethod:
2034
for inv, revid in self._iter_inventories_vfs(revision_ids, ordering):
2038
if order_as_requested:
2039
if next_revid is not None:
2040
yield None, next_revid
2043
yield invs.get(revid), revid
2046
yield None, missing.pop()
1289
2048
@needs_read_lock
1290
2049
def get_revision(self, revision_id):
1292
return self._real_repository.get_revision(revision_id)
2050
return self.get_revisions([revision_id])[0]
1294
2052
def get_transaction(self):
1295
2053
self._ensure_real()
1298
2056
@needs_read_lock
1299
2057
def clone(self, a_bzrdir, revision_id=None):
1301
return self._real_repository.clone(a_bzrdir, revision_id=revision_id)
2058
dest_repo = self._create_sprouting_repo(
2059
a_bzrdir, shared=self.is_shared())
2060
self.copy_content_into(dest_repo, revision_id)
1303
2063
def make_working_trees(self):
1304
2064
"""See Repository.make_working_trees"""
1306
return self._real_repository.make_working_trees()
2065
path = self.bzrdir._path_for_remote_call(self._client)
2067
response = self._call('Repository.make_working_trees', path)
2068
except errors.UnknownSmartMethod:
2070
return self._real_repository.make_working_trees()
2071
if response[0] not in ('yes', 'no'):
2072
raise SmartProtocolError('unexpected response code %s' % (response,))
2073
return response[0] == 'yes'
1308
2075
def refresh_data(self):
1309
"""Re-read any data needed to to synchronise with disk.
2076
"""Re-read any data needed to synchronise with disk.
1311
2078
This method is intended to be called after another repository instance
1312
2079
(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.
2080
repository. On all repositories this will work outside of write groups.
2081
Some repository formats (pack and newer for breezy native formats)
2082
support refresh_data inside write groups. If called inside a write
2083
group on a repository that does not support refreshing in a write group
2084
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
2086
if self._real_repository is not None:
1320
2087
self._real_repository.refresh_data()
2088
# Refresh the parents cache for this object
2089
self._unstacked_provider.disable_cache()
2090
self._unstacked_provider.enable_cache()
1322
2092
def revision_ids_to_search_result(self, result_set):
1323
2093
"""Convert a set of revision ids to a graph SearchResult."""
1324
2094
result_parents = set()
1325
for parents in self.get_graph().get_parent_map(
1326
result_set).itervalues():
2095
for parents in viewvalues(self.get_graph().get_parent_map(result_set)):
1327
2096
result_parents.update(parents)
1328
2097
included_keys = result_set.intersection(result_parents)
1329
2098
start_keys = result_set.difference(included_keys)
1330
2099
exclude_keys = result_parents.difference(result_set)
1331
result = graph.SearchResult(start_keys, exclude_keys,
2100
result = vf_search.SearchResult(start_keys, exclude_keys,
1332
2101
len(result_set), result_set)
1335
2104
@needs_read_lock
1336
def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
2105
def search_missing_revision_ids(self, other,
2106
find_ghosts=True, revision_ids=None, if_present_ids=None,
1337
2108
"""Return the revision ids that other has that this does not.
1339
2110
These are returned in topological order.
1341
2112
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)
2114
inter_repo = _mod_repository.InterRepository.get(other, self)
2115
return inter_repo.search_missing_revision_ids(
2116
find_ghosts=find_ghosts, revision_ids=revision_ids,
2117
if_present_ids=if_present_ids, limit=limit)
1346
def fetch(self, source, revision_id=None, pb=None, find_ghosts=False,
2119
def fetch(self, source, revision_id=None, find_ghosts=False,
1347
2120
fetch_spec=None):
1348
2121
# No base implementation to use as RemoteRepository is not a subclass
1349
2122
# of Repository; so this is a copy of Repository.fetch().
1388
2160
return self._real_repository._get_versioned_file_checker(
1389
2161
revisions, revision_versions_cache)
2163
def _iter_files_bytes_rpc(self, desired_files, absent):
2164
path = self.bzrdir._path_for_remote_call(self._client)
2167
for (file_id, revid, identifier) in desired_files:
2168
lines.append("%s\0%s" % (
2169
osutils.safe_file_id(file_id),
2170
osutils.safe_revision_id(revid)))
2171
identifiers.append(identifier)
2172
(response_tuple, response_handler) = (
2173
self._call_with_body_bytes_expecting_body(
2174
"Repository.iter_files_bytes", (path, ), "\n".join(lines)))
2175
if response_tuple != ('ok', ):
2176
response_handler.cancel_read_body()
2177
raise errors.UnexpectedSmartServerResponse(response_tuple)
2178
byte_stream = response_handler.read_streamed_body()
2179
def decompress_stream(start, byte_stream, unused):
2180
decompressor = zlib.decompressobj()
2181
yield decompressor.decompress(start)
2182
while decompressor.unused_data == "":
2184
data = next(byte_stream)
2185
except StopIteration:
2187
yield decompressor.decompress(data)
2188
yield decompressor.flush()
2189
unused.append(decompressor.unused_data)
2192
while not "\n" in unused:
2193
unused += next(byte_stream)
2194
header, rest = unused.split("\n", 1)
2195
args = header.split("\0")
2196
if args[0] == "absent":
2197
absent[identifiers[int(args[3])]] = (args[1], args[2])
2200
elif args[0] == "ok":
2203
raise errors.UnexpectedSmartServerResponse(args)
2205
yield (identifiers[idx],
2206
decompress_stream(rest, byte_stream, unused_chunks))
2207
unused = "".join(unused_chunks)
1391
2209
def iter_files_bytes(self, desired_files):
1392
2210
"""See Repository.iter_file_bytes.
1395
return self._real_repository.iter_files_bytes(desired_files)
2214
for (identifier, bytes_iterator) in self._iter_files_bytes_rpc(
2215
desired_files, absent):
2216
yield identifier, bytes_iterator
2217
for fallback in self._fallback_repositories:
2220
desired_files = [(key[0], key[1], identifier)
2221
for identifier, key in viewitems(absent)]
2222
for (identifier, bytes_iterator) in fallback.iter_files_bytes(desired_files):
2223
del absent[identifier]
2224
yield identifier, bytes_iterator
2226
# There may be more missing items, but raise an exception
2228
missing_identifier = next(iter(absent))
2229
missing_key = absent[missing_identifier]
2230
raise errors.RevisionNotPresent(revision_id=missing_key[1],
2231
file_id=missing_key[0])
2232
except errors.UnknownSmartMethod:
2234
for (identifier, bytes_iterator) in (
2235
self._real_repository.iter_files_bytes(desired_files)):
2236
yield identifier, bytes_iterator
2238
def get_cached_parent_map(self, revision_ids):
2239
"""See breezy.CachingParentsProvider.get_cached_parent_map"""
2240
return self._unstacked_provider.get_cached_parent_map(revision_ids)
1397
2242
def get_parent_map(self, revision_ids):
1398
"""See bzrlib.Graph.get_parent_map()."""
2243
"""See breezy.Graph.get_parent_map()."""
1399
2244
return self._make_parents_provider().get_parent_map(revision_ids)
1401
2246
def _get_parent_map_rpc(self, keys):
1532
2366
@needs_read_lock
1533
2367
def get_signature_text(self, revision_id):
1535
return self._real_repository.get_signature_text(revision_id)
2368
path = self.bzrdir._path_for_remote_call(self._client)
2370
response_tuple, response_handler = self._call_expecting_body(
2371
'Repository.get_revision_signature_text', path, revision_id)
2372
except errors.UnknownSmartMethod:
2374
return self._real_repository.get_signature_text(revision_id)
2375
except errors.NoSuchRevision as err:
2376
for fallback in self._fallback_repositories:
2378
return fallback.get_signature_text(revision_id)
2379
except errors.NoSuchRevision:
2383
if response_tuple[0] != 'ok':
2384
raise errors.UnexpectedSmartServerResponse(response_tuple)
2385
return response_handler.read_body_bytes()
1537
2387
@needs_read_lock
1538
2388
def _get_inventory_xml(self, revision_id):
2389
# This call is used by older working tree formats,
2390
# which stored a serialized basis inventory.
1539
2391
self._ensure_real()
1540
2392
return self._real_repository._get_inventory_xml(revision_id)
1542
2395
def reconcile(self, other=None, thorough=False):
1544
return self._real_repository.reconcile(other=other, thorough=thorough)
2396
from .reconcile import RepoReconciler
2397
path = self.bzrdir._path_for_remote_call(self._client)
2399
response, handler = self._call_expecting_body(
2400
'Repository.reconcile', path, self._lock_token)
2401
except (errors.UnknownSmartMethod, errors.TokenLockingNotSupported):
2403
return self._real_repository.reconcile(other=other, thorough=thorough)
2404
if response != ('ok', ):
2405
raise errors.UnexpectedSmartServerResponse(response)
2406
body = handler.read_body_bytes()
2407
result = RepoReconciler(self)
2408
for line in body.split('\n'):
2411
key, val_text = line.split(':')
2412
if key == "garbage_inventories":
2413
result.garbage_inventories = int(val_text)
2414
elif key == "inconsistent_parents":
2415
result.inconsistent_parents = int(val_text)
2417
mutter("unknown reconcile key %r" % key)
1546
2420
def all_revision_ids(self):
1548
return self._real_repository.all_revision_ids()
2421
path = self.bzrdir._path_for_remote_call(self._client)
2423
response_tuple, response_handler = self._call_expecting_body(
2424
"Repository.all_revision_ids", path)
2425
except errors.UnknownSmartMethod:
2427
return self._real_repository.all_revision_ids()
2428
if response_tuple != ("ok", ):
2429
raise errors.UnexpectedSmartServerResponse(response_tuple)
2430
revids = set(response_handler.read_body_bytes().splitlines())
2431
for fallback in self._fallback_repositories:
2432
revids.update(set(fallback.all_revision_ids()))
2435
def _filtered_revision_trees(self, revision_ids, file_ids):
2436
"""Return Tree for a revision on this branch with only some files.
2438
:param revision_ids: a sequence of revision-ids;
2439
a revision-id may not be None or 'null:'
2440
:param file_ids: if not None, the result is filtered
2441
so that only those file-ids, their parents and their
2442
children are included.
2444
inventories = self.iter_inventories(revision_ids)
2445
for inv in inventories:
2446
# Should we introduce a FilteredRevisionTree class rather
2447
# than pre-filter the inventory here?
2448
filtered_inv = inv.filter(file_ids)
2449
yield InventoryRevisionTree(self, filtered_inv, filtered_inv.revision_id)
1550
2451
@needs_read_lock
1551
2452
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)
2453
medium = self._client._medium
2454
if medium._is_remote_before((1, 2)):
2456
for delta in self._real_repository.get_deltas_for_revisions(
2457
revisions, specific_fileids):
2460
# Get the revision-ids of interest
2461
required_trees = set()
2462
for revision in revisions:
2463
required_trees.add(revision.revision_id)
2464
required_trees.update(revision.parent_ids[:1])
2466
# Get the matching filtered trees. Note that it's more
2467
# efficient to pass filtered trees to changes_from() rather
2468
# than doing the filtering afterwards. changes_from() could
2469
# arguably do the filtering itself but it's path-based, not
2470
# file-id based, so filtering before or afterwards is
2472
if specific_fileids is None:
2473
trees = dict((t.get_revision_id(), t) for
2474
t in self.revision_trees(required_trees))
2476
trees = dict((t.get_revision_id(), t) for
2477
t in self._filtered_revision_trees(required_trees,
2480
# Calculate the deltas
2481
for revision in revisions:
2482
if not revision.parent_ids:
2483
old_tree = self.revision_tree(_mod_revision.NULL_REVISION)
2485
old_tree = trees[revision.parent_ids[0]]
2486
yield trees[revision.revision_id].changes_from(old_tree)
1556
2488
@needs_read_lock
1557
2489
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)
2490
r = self.get_revision(revision_id)
2491
return list(self.get_deltas_for_revisions([r],
2492
specific_fileids=specific_fileids))[0]
1562
2494
@needs_read_lock
1563
2495
def revision_trees(self, revision_ids):
1565
return self._real_repository.revision_trees(revision_ids)
2496
inventories = self.iter_inventories(revision_ids)
2497
for inv in inventories:
2498
yield InventoryRevisionTree(self, inv, inv.revision_id)
1567
2500
@needs_read_lock
1568
2501
def get_revision_reconcile(self, revision_id):
1680
2626
self._ensure_real()
1681
2627
return self._real_repository.texts
2629
def _iter_revisions_rpc(self, revision_ids):
2630
body = "\n".join(revision_ids)
2631
path = self.bzrdir._path_for_remote_call(self._client)
2632
response_tuple, response_handler = (
2633
self._call_with_body_bytes_expecting_body(
2634
"Repository.iter_revisions", (path, ), body))
2635
if response_tuple[0] != "ok":
2636
raise errors.UnexpectedSmartServerResponse(response_tuple)
2637
serializer_format = response_tuple[1]
2638
serializer = serializer_format_registry.get(serializer_format)
2639
byte_stream = response_handler.read_streamed_body()
2640
decompressor = zlib.decompressobj()
2642
for bytes in byte_stream:
2643
chunks.append(decompressor.decompress(bytes))
2644
if decompressor.unused_data != "":
2645
chunks.append(decompressor.flush())
2646
yield serializer.read_revision_from_string("".join(chunks))
2647
unused = decompressor.unused_data
2648
decompressor = zlib.decompressobj()
2649
chunks = [decompressor.decompress(unused)]
2650
chunks.append(decompressor.flush())
2651
text = "".join(chunks)
2653
yield serializer.read_revision_from_string("".join(chunks))
1683
2655
@needs_read_lock
1684
2656
def get_revisions(self, revision_ids):
1686
return self._real_repository.get_revisions(revision_ids)
2657
if revision_ids is None:
2658
revision_ids = self.all_revision_ids()
2660
for rev_id in revision_ids:
2661
if not rev_id or not isinstance(rev_id, basestring):
2662
raise errors.InvalidRevisionId(
2663
revision_id=rev_id, branch=self)
2665
missing = set(revision_ids)
2667
for rev in self._iter_revisions_rpc(revision_ids):
2668
missing.remove(rev.revision_id)
2669
revs[rev.revision_id] = rev
2670
except errors.UnknownSmartMethod:
2672
return self._real_repository.get_revisions(revision_ids)
2673
for fallback in self._fallback_repositories:
2676
for revid in list(missing):
2677
# XXX JRV 2011-11-20: It would be nice if there was a
2678
# public method on Repository that could be used to query
2679
# for revision objects *without* failing completely if one
2680
# was missing. There is VersionedFileRepository._iter_revisions,
2681
# but unfortunately that's private and not provided by
2682
# all repository implementations.
2684
revs[revid] = fallback.get_revision(revid)
2685
except errors.NoSuchRevision:
2688
missing.remove(revid)
2690
raise errors.NoSuchRevision(self, list(missing)[0])
2691
return [revs[revid] for revid in revision_ids]
1688
2693
def supports_rich_root(self):
1689
2694
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
2697
def _serializer(self):
1697
2698
return self._format._serializer
1699
2701
def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
1701
return self._real_repository.store_revision_signature(
1702
gpg_strategy, plaintext, revision_id)
2702
signature = gpg_strategy.sign(plaintext)
2703
self.add_signature_text(revision_id, signature)
1704
2705
def add_signature_text(self, revision_id, signature):
1706
return self._real_repository.add_signature_text(revision_id, signature)
2706
if self._real_repository:
2707
# If there is a real repository the write group will
2708
# be in the real repository as well, so use that:
2710
return self._real_repository.add_signature_text(
2711
revision_id, signature)
2712
path = self.bzrdir._path_for_remote_call(self._client)
2713
response, handler = self._call_with_body_bytes_expecting_body(
2714
'Repository.add_signature_text', (path, self._lock_token,
2715
revision_id) + tuple(self._write_group_tokens), signature)
2716
handler.cancel_read_body()
2718
if response[0] != 'ok':
2719
raise errors.UnexpectedSmartServerResponse(response)
2720
self._write_group_tokens = response[1:]
1708
2722
def has_signature_for_revision_id(self, revision_id):
1710
return self._real_repository.has_signature_for_revision_id(revision_id)
2723
path = self.bzrdir._path_for_remote_call(self._client)
2725
response = self._call('Repository.has_signature_for_revision_id',
2727
except errors.UnknownSmartMethod:
2729
return self._real_repository.has_signature_for_revision_id(
2731
if response[0] not in ('yes', 'no'):
2732
raise SmartProtocolError('unexpected response code %s' % (response,))
2733
if response[0] == 'yes':
2735
for fallback in self._fallback_repositories:
2736
if fallback.has_signature_for_revision_id(revision_id):
2741
def verify_revision_signature(self, revision_id, gpg_strategy):
2742
if not self.has_signature_for_revision_id(revision_id):
2743
return gpg.SIGNATURE_NOT_SIGNED, None
2744
signature = self.get_signature_text(revision_id)
2746
testament = _mod_testament.Testament.from_revision(self, revision_id)
2747
plaintext = testament.as_short_text()
2749
return gpg_strategy.verify(signature, plaintext)
1712
2751
def item_keys_introduced_by(self, revision_ids, _files_pb=None):
1713
2752
self._ensure_real()
1714
2753
return self._real_repository.item_keys_introduced_by(revision_ids,
1715
2754
_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
2756
def _find_inconsistent_revision_parents(self, revisions_iterator=None):
1723
2757
self._ensure_real()
1724
2758
return self._real_repository._find_inconsistent_revision_parents(
2071
3117
if isinstance(a_bzrdir, RemoteBzrDir):
2072
3118
a_bzrdir._ensure_real()
2073
3119
result = self._custom_format.initialize(a_bzrdir._real_bzrdir,
3120
name=name, append_revisions_only=append_revisions_only,
3121
repository=repository)
2076
3123
# We assume the bzrdir is parameterised; it may not be.
2077
result = self._custom_format.initialize(a_bzrdir, name)
3124
result = self._custom_format.initialize(a_bzrdir, name=name,
3125
append_revisions_only=append_revisions_only,
3126
repository=repository)
2078
3127
if (isinstance(a_bzrdir, RemoteBzrDir) and
2079
3128
not isinstance(result, RemoteBranch)):
2080
3129
result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result,
2084
def initialize(self, a_bzrdir, name=None):
3133
def initialize(self, a_bzrdir, name=None, repository=None,
3134
append_revisions_only=None):
3136
name = a_bzrdir._get_selected_branch()
2085
3137
# 1) get the network name to use.
2086
3138
if self._custom_format:
2087
3139
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')()
3141
# Select the current breezy default and ask for that.
3142
reference_bzrdir_format = controldir.format_registry.get('default')()
2091
3143
reference_format = reference_bzrdir_format.get_branch_format()
2092
3144
self._custom_format = reference_format
2093
3145
network_name = reference_format.network_name()
2094
3146
# Being asked to create on a non RemoteBzrDir:
2095
3147
if not isinstance(a_bzrdir, RemoteBzrDir):
2096
return self._vfs_initialize(a_bzrdir, name=name)
3148
return self._vfs_initialize(a_bzrdir, name=name,
3149
append_revisions_only=append_revisions_only,
3150
repository=repository)
2097
3151
medium = a_bzrdir._client._medium
2098
3152
if medium._is_remote_before((1, 13)):
2099
return self._vfs_initialize(a_bzrdir, name=name)
3153
return self._vfs_initialize(a_bzrdir, name=name,
3154
append_revisions_only=append_revisions_only,
3155
repository=repository)
2100
3156
# Creating on a remote bzr dir.
2101
3157
# 2) try direct creation via RPC
2102
3158
path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
2103
if name is not None:
2104
3160
# XXX JRV20100304: Support creating colocated branches
2105
3161
raise errors.NoColocatedBranchSupport(self)
2106
3162
verb = 'BzrDir.create_branch'
2148
3220
self._ensure_real()
2149
3221
return self._custom_format.supports_set_append_revisions_only()
3223
def _use_default_local_heads_to_fetch(self):
3224
# If the branch format is a metadir format *and* its heads_to_fetch
3225
# implementation is not overridden vs the base class, we can use the
3226
# base class logic rather than use the heads_to_fetch RPC. This is
3227
# usually cheaper in terms of net round trips, as the last-revision and
3228
# tags info fetched is cached and would be fetched anyway.
3230
if isinstance(self._custom_format, branch.BranchFormatMetadir):
3231
branch_class = self._custom_format._branch_class()
3232
heads_to_fetch_impl = branch_class.heads_to_fetch.__func__
3233
if heads_to_fetch_impl is branch.Branch.heads_to_fetch.__func__:
3238
class RemoteBranchStore(_mod_config.IniFileStore):
3239
"""Branch store which attempts to use HPSS calls to retrieve branch store.
3241
Note that this is specific to bzr-based formats.
3244
def __init__(self, branch):
3245
super(RemoteBranchStore, self).__init__()
3246
self.branch = branch
3248
self._real_store = None
3250
def external_url(self):
3251
return urlutils.join(self.branch.user_url, 'branch.conf')
3253
def _load_content(self):
3254
path = self.branch._remote_path()
3256
response, handler = self.branch._call_expecting_body(
3257
'Branch.get_config_file', path)
3258
except errors.UnknownSmartMethod:
3260
return self._real_store._load_content()
3261
if len(response) and response[0] != 'ok':
3262
raise errors.UnexpectedSmartServerResponse(response)
3263
return handler.read_body_bytes()
3265
def _save_content(self, content):
3266
path = self.branch._remote_path()
3268
response, handler = self.branch._call_with_body_bytes_expecting_body(
3269
'Branch.put_config_file', (path,
3270
self.branch._lock_token, self.branch._repo_lock_token),
3272
except errors.UnknownSmartMethod:
3274
return self._real_store._save_content(content)
3275
handler.cancel_read_body()
3276
if response != ('ok', ):
3277
raise errors.UnexpectedSmartServerResponse(response)
3279
def _ensure_real(self):
3280
self.branch._ensure_real()
3281
if self._real_store is None:
3282
self._real_store = _mod_config.BranchStore(self.branch)
2152
3285
class RemoteBranch(branch.Branch, _RpcHelper, lock._RelockDebugMixin):
2153
3286
"""Branch stored on a server accessed by HPSS RPC.
2654
3836
_override_hook_target=self, **kwargs)
2656
3838
@needs_read_lock
2657
def push(self, target, overwrite=False, stop_revision=None):
3839
def push(self, target, overwrite=False, stop_revision=None, lossy=False):
2658
3840
self._ensure_real()
2659
3841
return self._real_branch.push(
2660
target, overwrite=overwrite, stop_revision=stop_revision,
3842
target, overwrite=overwrite, stop_revision=stop_revision, lossy=lossy,
2661
3843
_override_hook_source_branch=self)
3845
def peek_lock_mode(self):
3846
return self._lock_mode
2663
3848
def is_locked(self):
2664
3849
return self._lock_count >= 1
2666
3851
@needs_read_lock
3852
def revision_id_to_dotted_revno(self, revision_id):
3853
"""Given a revision id, return its dotted revno.
3855
:return: a tuple like (1,) or (400,1,3).
3858
response = self._call('Branch.revision_id_to_revno',
3859
self._remote_path(), revision_id)
3860
except errors.UnknownSmartMethod:
3862
return self._real_branch.revision_id_to_dotted_revno(revision_id)
3863
if response[0] == 'ok':
3864
return tuple([int(x) for x in response[1:]])
3866
raise errors.UnexpectedSmartServerResponse(response)
2667
3869
def revision_id_to_revno(self, revision_id):
2669
return self._real_branch.revision_id_to_revno(revision_id)
3870
"""Given a revision id on the branch mainline, return its revno.
3875
response = self._call('Branch.revision_id_to_revno',
3876
self._remote_path(), revision_id)
3877
except errors.UnknownSmartMethod:
3879
return self._real_branch.revision_id_to_revno(revision_id)
3880
if response[0] == 'ok':
3881
if len(response) == 2:
3882
return int(response[1])
3883
raise NoSuchRevision(self, revision_id)
3885
raise errors.UnexpectedSmartServerResponse(response)
2671
3887
@needs_write_lock
2672
3888
def set_last_revision_info(self, revno, revision_id):
2673
3889
# XXX: These should be returned by the set_last_revision_info verb
2674
3890
old_revno, old_revid = self.last_revision_info()
2675
3891
self._run_pre_change_branch_tip_hooks(revno, revision_id)
2676
revision_id = ensure_null(revision_id)
3892
if not revision_id or not isinstance(revision_id, basestring):
3893
raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2678
3895
response = self._call('Branch.set_last_revision_info',
2679
3896
self._remote_path(), self._lock_token, self._repo_lock_token,
2708
3925
except errors.UnknownSmartMethod:
2709
3926
medium._remember_remote_is_before((1, 6))
2710
3927
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))
3928
graph = self.repository.get_graph()
3929
(last_revno, last_revid) = self.last_revision_info()
3930
known_revision_ids = [
3931
(last_revid, last_revno),
3932
(_mod_revision.NULL_REVISION, 0),
3934
if last_rev is not None:
3935
if not graph.is_ancestor(last_rev, revision_id):
3936
# our previous tip is not merged into stop_revision
3937
raise errors.DivergedBranches(self, other_branch)
3938
revno = graph.find_distance_to_null(revision_id, known_revision_ids)
3939
self.set_last_revision_info(revno, revision_id)
2714
3941
def set_push_location(self, location):
3942
self._set_config_location('push_location', location)
3944
def heads_to_fetch(self):
3945
if self._format._use_default_local_heads_to_fetch():
3946
# We recognise this format, and its heads-to-fetch implementation
3947
# is the default one (tip + tags). In this case it's cheaper to
3948
# just use the default implementation rather than a special RPC as
3949
# the tip and tags data is cached.
3950
return branch.Branch.heads_to_fetch(self)
3951
medium = self._client._medium
3952
if medium._is_remote_before((2, 4)):
3953
return self._vfs_heads_to_fetch()
3955
return self._rpc_heads_to_fetch()
3956
except errors.UnknownSmartMethod:
3957
medium._remember_remote_is_before((2, 4))
3958
return self._vfs_heads_to_fetch()
3960
def _rpc_heads_to_fetch(self):
3961
response = self._call('Branch.heads_to_fetch', self._remote_path())
3962
if len(response) != 2:
3963
raise errors.UnexpectedSmartServerResponse(response)
3964
must_fetch, if_present_fetch = response
3965
return set(must_fetch), set(if_present_fetch)
3967
def _vfs_heads_to_fetch(self):
2715
3968
self._ensure_real()
2716
return self._real_branch.set_push_location(location)
3969
return self._real_branch.heads_to_fetch()
2719
3972
class RemoteConfig(object):
2774
4037
medium = self._branch._client._medium
2775
4038
if medium._is_remote_before((1, 14)):
2776
4039
return self._vfs_set_option(value, name, section)
4040
if isinstance(value, dict):
4041
if medium._is_remote_before((2, 2)):
4042
return self._vfs_set_option(value, name, section)
4043
return self._set_config_option_dict(value, name, section)
4045
return self._set_config_option(value, name, section)
4047
def _set_config_option(self, value, name, section):
2778
4049
path = self._branch._remote_path()
2779
4050
response = self._branch._client.call('Branch.set_config_option',
2780
4051
path, self._branch._lock_token, self._branch._repo_lock_token,
2781
4052
value.encode('utf8'), name, section or '')
2782
4053
except errors.UnknownSmartMethod:
4054
medium = self._branch._client._medium
2783
4055
medium._remember_remote_is_before((1, 14))
2784
4056
return self._vfs_set_option(value, name, section)
2785
4057
if response != ():
2786
4058
raise errors.UnexpectedSmartServerResponse(response)
4060
def _serialize_option_dict(self, option_dict):
4062
for key, value in option_dict.items():
4063
if isinstance(key, unicode):
4064
key = key.encode('utf8')
4065
if isinstance(value, unicode):
4066
value = value.encode('utf8')
4067
utf8_dict[key] = value
4068
return bencode.bencode(utf8_dict)
4070
def _set_config_option_dict(self, value, name, section):
4072
path = self._branch._remote_path()
4073
serialised_dict = self._serialize_option_dict(value)
4074
response = self._branch._client.call(
4075
'Branch.set_config_option_dict',
4076
path, self._branch._lock_token, self._branch._repo_lock_token,
4077
serialised_dict, name, section or '')
4078
except errors.UnknownSmartMethod:
4079
medium = self._branch._client._medium
4080
medium._remember_remote_is_before((2, 2))
4081
return self._vfs_set_option(value, name, section)
4083
raise errors.UnexpectedSmartServerResponse(response)
2788
4085
def _real_object(self):
2789
4086
self._branch._ensure_real()
2790
4087
return self._branch._real_branch
2867
4167
return context['path']
2868
except KeyError, key_err:
4168
except KeyError as key_err:
2870
4170
return err.error_args[0]
2871
except IndexError, idx_err:
4171
except IndexError as idx_err:
2873
4173
'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'):
4177
translator = error_translators.get(err.error_verb)
4181
raise translator(err, find, get_path)
4183
translator = no_context_error_translators.get(err.error_verb)
4185
raise errors.UnknownErrorFromSmartServer(err)
4187
raise translator(err)
4190
error_translators.register('NoSuchRevision',
4191
lambda err, find, get_path: NoSuchRevision(
4192
find('branch'), err.error_args[0]))
4193
error_translators.register('nosuchrevision',
4194
lambda err, find, get_path: NoSuchRevision(
4195
find('repository'), err.error_args[0]))
4197
def _translate_nobranch_error(err, find, get_path):
4198
if len(err.error_args) >= 1:
4199
extra = err.error_args[0]
4202
return errors.NotBranchError(path=find('bzrdir').root_transport.base,
4205
error_translators.register('nobranch', _translate_nobranch_error)
4206
error_translators.register('norepository',
4207
lambda err, find, get_path: errors.NoRepositoryPresent(
4209
error_translators.register('UnlockableTransport',
4210
lambda err, find, get_path: errors.UnlockableTransport(
4211
find('bzrdir').root_transport))
4212
error_translators.register('TokenMismatch',
4213
lambda err, find, get_path: errors.TokenMismatch(
4214
find('token'), '(remote token)'))
4215
error_translators.register('Diverged',
4216
lambda err, find, get_path: errors.DivergedBranches(
4217
find('branch'), find('other_branch')))
4218
error_translators.register('NotStacked',
4219
lambda err, find, get_path: errors.NotStacked(branch=find('branch')))
4221
def _translate_PermissionDenied(err, find, get_path):
4223
if len(err.error_args) >= 2:
4224
extra = err.error_args[1]
4227
return errors.PermissionDenied(path, extra=extra)
4229
error_translators.register('PermissionDenied', _translate_PermissionDenied)
4230
error_translators.register('ReadError',
4231
lambda err, find, get_path: errors.ReadError(get_path()))
4232
error_translators.register('NoSuchFile',
4233
lambda err, find, get_path: errors.NoSuchFile(get_path()))
4234
error_translators.register('TokenLockingNotSupported',
4235
lambda err, find, get_path: errors.TokenLockingNotSupported(
4236
find('repository')))
4237
error_translators.register('UnsuspendableWriteGroup',
4238
lambda err, find, get_path: errors.UnsuspendableWriteGroup(
4239
repository=find('repository')))
4240
error_translators.register('UnresumableWriteGroup',
4241
lambda err, find, get_path: errors.UnresumableWriteGroup(
4242
repository=find('repository'), write_groups=err.error_args[0],
4243
reason=err.error_args[1]))
4244
no_context_error_translators.register('IncompatibleRepositories',
4245
lambda err: errors.IncompatibleRepositories(
4246
err.error_args[0], err.error_args[1], err.error_args[2]))
4247
no_context_error_translators.register('LockContention',
4248
lambda err: errors.LockContention('(remote lock)'))
4249
no_context_error_translators.register('LockFailed',
4250
lambda err: errors.LockFailed(err.error_args[0], err.error_args[1]))
4251
no_context_error_translators.register('TipChangeRejected',
4252
lambda err: errors.TipChangeRejected(err.error_args[0].decode('utf8')))
4253
no_context_error_translators.register('UnstackableBranchFormat',
4254
lambda err: errors.UnstackableBranchFormat(*err.error_args))
4255
no_context_error_translators.register('UnstackableRepositoryFormat',
4256
lambda err: errors.UnstackableRepositoryFormat(*err.error_args))
4257
no_context_error_translators.register('FileExists',
4258
lambda err: errors.FileExists(err.error_args[0]))
4259
no_context_error_translators.register('DirectoryNotEmpty',
4260
lambda err: errors.DirectoryNotEmpty(err.error_args[0]))
4262
def _translate_short_readv_error(err):
4263
args = err.error_args
4264
return errors.ShortReadvError(args[0], int(args[1]), int(args[2]),
4267
no_context_error_translators.register('ShortReadvError',
4268
_translate_short_readv_error)
4270
def _translate_unicode_error(err):
2932
4271
encoding = str(err.error_args[0]) # encoding must always be a string
2933
4272
val = err.error_args[1]
2934
4273
start = int(err.error_args[2])