14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
# TODO: At some point, handle upgrades by just passing the whole request
18
# across to run on the server.
19
22
from bzrlib import (
30
repository as _mod_repository,
32
33
revision as _mod_revision,
36
from bzrlib.branch import BranchReferenceFormat, BranchWriteLockResult
37
from bzrlib.branch import BranchReferenceFormat
37
38
from bzrlib.bzrdir import BzrDir, RemoteBzrDirFormat
38
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
39
from bzrlib.decorators import needs_read_lock, needs_write_lock
39
40
from bzrlib.errors import (
41
42
SmartProtocolError,
62
63
except errors.ErrorFromSmartServer, err:
63
64
self._translate_error(err, **err_context)
65
def _call_with_body_bytes(self, method, args, body_bytes, **err_context):
67
return self._client.call_with_body_bytes(method, args, body_bytes)
68
except errors.ErrorFromSmartServer, err:
69
self._translate_error(err, **err_context)
71
66
def _call_with_body_bytes_expecting_body(self, method, args, body_bytes,
112
106
self._client = client._SmartClient(medium)
114
108
self._client = _client
121
return '%s(%r)' % (self.__class__.__name__, self._client)
123
def _probe_bzrdir(self):
124
medium = self._client._medium
125
111
path = self._path_for_remote_call(self._client)
126
if medium._is_remote_before((2, 1)):
130
self._rpc_open_2_1(path)
132
except errors.UnknownSmartMethod:
133
medium._remember_remote_is_before((2, 1))
136
def _rpc_open_2_1(self, path):
137
response = self._call('BzrDir.open_2.1', path)
138
if response == ('no',):
139
raise errors.NotBranchError(path=self.root_transport.base)
140
elif response[0] == 'yes':
141
if response[1] == 'yes':
142
self._has_working_tree = True
143
elif response[1] == 'no':
144
self._has_working_tree = False
146
raise errors.UnexpectedSmartServerResponse(response)
148
raise errors.UnexpectedSmartServerResponse(response)
150
def _rpc_open(self, path):
151
112
response = self._call('BzrDir.open', path)
152
113
if response not in [('yes',), ('no',)]:
153
114
raise errors.UnexpectedSmartServerResponse(response)
154
115
if response == ('no',):
155
raise errors.NotBranchError(path=self.root_transport.base)
116
raise errors.NotBranchError(path=transport.base)
157
118
def _ensure_real(self):
158
119
"""Ensure that there is a _real_bzrdir set.
245
202
self._ensure_real()
246
203
self._real_bzrdir.destroy_repository()
248
def create_branch(self, name=None):
205
def create_branch(self):
249
206
# as per meta1 formats - just delegate to the format object which may
250
207
# be parameterised.
251
real_branch = self._format.get_branch_format().initialize(self,
208
real_branch = self._format.get_branch_format().initialize(self)
253
209
if not isinstance(real_branch, RemoteBranch):
254
result = RemoteBranch(self, self.find_repository(), real_branch,
210
result = RemoteBranch(self, self.find_repository(), real_branch)
257
212
result = real_branch
258
213
# BzrDir.clone_on_transport() uses the result of create_branch but does
264
219
self._next_open_branch_result = result
267
def destroy_branch(self, name=None):
222
def destroy_branch(self):
268
223
"""See BzrDir.destroy_branch"""
269
224
self._ensure_real()
270
self._real_bzrdir.destroy_branch(name=name)
225
self._real_bzrdir.destroy_branch()
271
226
self._next_open_branch_result = None
273
228
def create_workingtree(self, revision_id=None, from_branch=None):
274
229
raise errors.NotLocalUrl(self.transport.base)
276
def find_branch_format(self, name=None):
231
def find_branch_format(self):
277
232
"""Find the branch 'format' for this bzrdir.
279
234
This might be a synthetic object for e.g. RemoteBranch and SVN.
281
b = self.open_branch(name=name)
236
b = self.open_branch()
284
def get_branch_reference(self, name=None):
239
def get_branch_reference(self):
285
240
"""See BzrDir.get_branch_reference()."""
287
# XXX JRV20100304: Support opening colocated branches
288
raise errors.NoColocatedBranchSupport(self)
289
241
response = self._get_branch_reference()
290
242
if response[0] == 'ref':
291
243
return response[1]
295
247
def _get_branch_reference(self):
296
248
path = self._path_for_remote_call(self._client)
297
249
medium = self._client._medium
299
('BzrDir.open_branchV3', (2, 1)),
300
('BzrDir.open_branchV2', (1, 13)),
301
('BzrDir.open_branch', None),
303
for verb, required_version in candidate_calls:
304
if required_version and medium._is_remote_before(required_version):
250
if not medium._is_remote_before((1, 13)):
307
response = self._call(verb, path)
252
response = self._call('BzrDir.open_branchV2', path)
253
if response[0] not in ('ref', 'branch'):
254
raise errors.UnexpectedSmartServerResponse(response)
308
256
except errors.UnknownSmartMethod:
309
if required_version is None:
311
medium._remember_remote_is_before(required_version)
314
if verb == 'BzrDir.open_branch':
315
if response[0] != 'ok':
316
raise errors.UnexpectedSmartServerResponse(response)
317
if response[1] != '':
318
return ('ref', response[1])
320
return ('branch', '')
321
if response[0] not in ('ref', 'branch'):
257
medium._remember_remote_is_before((1, 13))
258
response = self._call('BzrDir.open_branch', path)
259
if response[0] != 'ok':
322
260
raise errors.UnexpectedSmartServerResponse(response)
261
if response[1] != '':
262
return ('ref', response[1])
264
return ('branch', '')
325
def _get_tree_branch(self, name=None):
266
def _get_tree_branch(self):
326
267
"""See BzrDir._get_tree_branch()."""
327
return None, self.open_branch(name=name)
268
return None, self.open_branch()
329
def open_branch(self, name=None, unsupported=False,
330
ignore_fallbacks=False):
270
def open_branch(self, _unsupported=False, ignore_fallbacks=False):
332
272
raise NotImplementedError('unsupported flag support not implemented yet.')
333
273
if self._next_open_branch_result is not None:
334
274
# See create_branch for details.
339
279
if response[0] == 'ref':
340
280
# a branch reference, use the existing BranchReference logic.
341
281
format = BranchReferenceFormat()
342
return format.open(self, name=name, _found=True,
343
location=response[1], ignore_fallbacks=ignore_fallbacks)
282
return format.open(self, _found=True, location=response[1],
283
ignore_fallbacks=ignore_fallbacks)
344
284
branch_format_name = response[1]
345
285
if not branch_format_name:
346
286
branch_format_name = None
347
287
format = RemoteBranchFormat(network_name=branch_format_name)
348
288
return RemoteBranch(self, self.find_repository(), format=format,
349
setup_stacking=not ignore_fallbacks, name=name)
289
setup_stacking=not ignore_fallbacks)
351
291
def _open_repo_v1(self, path):
352
292
verb = 'BzrDir.find_repository'
624
547
return self._custom_format._fetch_reconcile
626
549
def get_format_description(self):
628
return 'Remote: ' + self._custom_format.get_format_description()
550
return 'bzr remote repository'
630
552
def __eq__(self, other):
631
553
return self.__class__ is other.__class__
555
def check_conversion_target(self, target_format):
556
if self.rich_root_data and not target_format.rich_root_data:
557
raise errors.BadConversionTarget(
558
'Does not support rich root data.', target_format)
559
if (self.supports_tree_reference and
560
not getattr(target_format, 'supports_tree_reference', False)):
561
raise errors.BadConversionTarget(
562
'Does not support nested trees', target_format)
633
564
def network_name(self):
634
565
if self._network_name:
635
566
return self._network_name
756
670
self._ensure_real()
757
671
return self._real_repository.suspend_write_group()
759
def get_missing_parent_inventories(self, check_for_missing_texts=True):
761
return self._real_repository.get_missing_parent_inventories(
762
check_for_missing_texts=check_for_missing_texts)
764
def _get_rev_id_for_revno_vfs(self, revno, known_pair):
766
return self._real_repository.get_rev_id_for_revno(
769
def get_rev_id_for_revno(self, revno, known_pair):
770
"""See Repository.get_rev_id_for_revno."""
771
path = self.bzrdir._path_for_remote_call(self._client)
773
if self._client._medium._is_remote_before((1, 17)):
774
return self._get_rev_id_for_revno_vfs(revno, known_pair)
775
response = self._call(
776
'Repository.get_rev_id_for_revno', path, revno, known_pair)
777
except errors.UnknownSmartMethod:
778
self._client._medium._remember_remote_is_before((1, 17))
779
return self._get_rev_id_for_revno_vfs(revno, known_pair)
780
if response[0] == 'ok':
781
return True, response[1]
782
elif response[0] == 'history-incomplete':
783
known_pair = response[1:3]
784
for fallback in self._fallback_repositories:
785
found, result = fallback.get_rev_id_for_revno(revno, known_pair)
790
# Not found in any fallbacks
791
return False, known_pair
793
raise errors.UnexpectedSmartServerResponse(response)
673
def get_missing_parent_inventories(self):
675
return self._real_repository.get_missing_parent_inventories()
795
677
def _ensure_real(self):
796
678
"""Ensure that there is a _real_repository set.
896
774
result.add(_mod_revision.NULL_REVISION)
899
def _has_same_fallbacks(self, other_repo):
900
"""Returns true if the repositories have the same fallbacks."""
901
# XXX: copied from Repository; it should be unified into a base class
902
# <https://bugs.launchpad.net/bzr/+bug/401622>
903
my_fb = self._fallback_repositories
904
other_fb = other_repo._fallback_repositories
905
if len(my_fb) != len(other_fb):
907
for f, g in zip(my_fb, other_fb):
908
if not f.has_same_location(g):
912
777
def has_same_location(self, other):
913
# TODO: Move to RepositoryBase and unify with the regular Repository
914
# one; unfortunately the tests rely on slightly different behaviour at
915
# present -- mbp 20090710
916
778
return (self.__class__ is other.__class__ and
917
779
self.bzrdir.transport.base == other.bzrdir.transport.base)
921
783
parents_provider = self._make_parents_provider(other_repository)
922
784
return graph.Graph(parents_provider)
925
def get_known_graph_ancestry(self, revision_ids):
926
"""Return the known graph for a set of revision ids and their ancestors.
928
st = static_tuple.StaticTuple
929
revision_keys = [st(r_id).intern() for r_id in revision_ids]
930
known_graph = self.revisions.get_known_graph_ancestry(revision_keys)
931
return graph.GraphThunkIdsToKeys(known_graph)
933
786
def gather_stats(self, revid=None, committers=None):
934
787
"""See Repository.gather_stats()."""
935
788
path = self.bzrdir._path_for_remote_call(self._client)
995
848
def is_write_locked(self):
996
849
return self._lock_mode == 'w'
998
def _warn_if_deprecated(self, branch=None):
999
# If we have a real repository, the check will be done there, if we
1000
# don't the check will be done remotely.
1003
851
def lock_read(self):
1004
"""Lock the repository for read operations.
1006
:return: A bzrlib.lock.LogicalLockResult.
1008
852
# wrong eventually - want a local lock cache context
1009
853
if not self._lock_mode:
1010
self._note_lock('r')
1011
854
self._lock_mode = 'r'
1012
855
self._lock_count = 1
1013
856
self._unstacked_provider.enable_cache(cache_misses=True)
1014
857
if self._real_repository is not None:
1015
858
self._real_repository.lock_read()
1016
for repo in self._fallback_repositories:
1019
860
self._lock_count += 1
1020
return lock.LogicalLockResult(self.unlock)
861
for repo in self._fallback_repositories:
1022
864
def _remote_lock_write(self, token):
1023
865
path = self.bzrdir._path_for_remote_call(self._client)
1244
1080
# We need to accumulate additional repositories here, to pass them in
1245
1081
# on various RPC's.
1247
if self.is_locked():
1248
# We will call fallback.unlock() when we transition to the unlocked
1249
# state, so always add a lock here. If a caller passes us a locked
1250
# repository, they are responsible for unlocking it later.
1251
repository.lock_read()
1252
self._check_fallback_repository(repository)
1253
1083
self._fallback_repositories.append(repository)
1254
1084
# If self._real_repository was parameterised already (e.g. because a
1255
1085
# _real_branch had its get_stacked_on_url method called), then the
1256
1086
# repository to be added may already be in the _real_repositories list.
1257
1087
if self._real_repository is not None:
1258
fallback_locations = [repo.user_url for repo in
1088
fallback_locations = [repo.bzrdir.root_transport.base for repo in
1259
1089
self._real_repository._fallback_repositories]
1260
if repository.user_url not in fallback_locations:
1090
if repository.bzrdir.root_transport.base not in fallback_locations:
1261
1091
self._real_repository.add_fallback_repository(repository)
1263
def _check_fallback_repository(self, repository):
1264
"""Check that this repository can fallback to repository safely.
1266
Raise an error if not.
1268
:param repository: A repository to fallback to.
1270
return _mod_repository.InterRepository._assert_same_model(
1273
1093
def add_inventory(self, revid, inv, parents):
1274
1094
self._ensure_real()
1275
1095
return self._real_repository.add_inventory(revid, inv, parents)
1277
1097
def add_inventory_by_delta(self, basis_revision_id, delta, new_revision_id,
1278
parents, basis_inv=None, propagate_caches=False):
1279
1099
self._ensure_real()
1280
1100
return self._real_repository.add_inventory_by_delta(basis_revision_id,
1281
delta, new_revision_id, parents, basis_inv=basis_inv,
1282
propagate_caches=propagate_caches)
1101
delta, new_revision_id, parents)
1284
1103
def add_revision(self, rev_id, rev, inv=None, config=None):
1285
1104
self._ensure_real()
1315
1134
return self._real_repository.make_working_trees()
1317
1136
def refresh_data(self):
1318
"""Re-read any data needed to synchronise with disk.
1137
"""Re-read any data needed to to synchronise with disk.
1320
1139
This method is intended to be called after another repository instance
1321
1140
(such as one used by a smart server) has inserted data into the
1322
repository. On all repositories this will work outside of write groups.
1323
Some repository formats (pack and newer for bzrlib native formats)
1324
support refresh_data inside write groups. If called inside a write
1325
group on a repository that does not support refreshing in a write group
1326
IsInWriteGroupError will be raised.
1141
repository. It may not be called during a write group, but may be
1142
called at any other time.
1144
if self.is_in_write_group():
1145
raise errors.InternalBzrError(
1146
"May not refresh_data while in a write group.")
1328
1147
if self._real_repository is not None:
1329
1148
self._real_repository.refresh_data()
1791
1610
def insert_stream(self, stream, src_format, resume_tokens):
1792
1611
target = self.target_repo
1793
1612
target._unstacked_provider.missing_keys.clear()
1794
candidate_calls = [('Repository.insert_stream_1.19', (1, 19))]
1795
1613
if target._lock_token:
1796
candidate_calls.append(('Repository.insert_stream_locked', (1, 14)))
1797
lock_args = (target._lock_token or '',)
1614
verb = 'Repository.insert_stream_locked'
1615
extra_args = (target._lock_token or '',)
1616
required_version = (1, 14)
1799
candidate_calls.append(('Repository.insert_stream', (1, 13)))
1618
verb = 'Repository.insert_stream'
1620
required_version = (1, 13)
1801
1621
client = target._client
1802
1622
medium = client._medium
1623
if medium._is_remote_before(required_version):
1624
# No possible way this can work.
1625
return self._insert_real(stream, src_format, resume_tokens)
1803
1626
path = target.bzrdir._path_for_remote_call(client)
1804
# Probe for the verb to use with an empty stream before sending the
1805
# real stream to it. We do this both to avoid the risk of sending a
1806
# large request that is then rejected, and because we don't want to
1807
# implement a way to buffer, rewind, or restart the stream.
1809
for verb, required_version in candidate_calls:
1810
if medium._is_remote_before(required_version):
1813
# We've already done the probing (and set _is_remote_before) on
1814
# a previous insert.
1627
if not resume_tokens:
1628
# XXX: Ugly but important for correctness, *will* be fixed during
1629
# 1.13 cycle. Pushing a stream that is interrupted results in a
1630
# fallback to the _real_repositories sink *with a partial stream*.
1631
# Thats bad because we insert less data than bzr expected. To avoid
1632
# this we do a trial push to make sure the verb is accessible, and
1633
# do not fallback when actually pushing the stream. A cleanup patch
1634
# is going to look at rewinding/restarting the stream/partial
1817
1636
byte_stream = smart_repo._stream_to_byte_stream([], src_format)
1819
1638
response = client.call_with_body_stream(
1820
(verb, path, '') + lock_args, byte_stream)
1639
(verb, path, '') + extra_args, byte_stream)
1821
1640
except errors.UnknownSmartMethod:
1822
1641
medium._remember_remote_is_before(required_version)
1828
return self._insert_real(stream, src_format, resume_tokens)
1829
self._last_inv_record = None
1830
self._last_substream = None
1831
if required_version < (1, 19):
1832
# Remote side doesn't support inventory deltas. Wrap the stream to
1833
# make sure we don't send any. If the stream contains inventory
1834
# deltas we'll interrupt the smart insert_stream request and
1836
stream = self._stop_stream_if_inventory_delta(stream)
1642
return self._insert_real(stream, src_format, resume_tokens)
1837
1643
byte_stream = smart_repo._stream_to_byte_stream(
1838
1644
stream, src_format)
1839
1645
resume_tokens = ' '.join(resume_tokens)
1840
1646
response = client.call_with_body_stream(
1841
(verb, path, resume_tokens) + lock_args, byte_stream)
1647
(verb, path, resume_tokens) + extra_args, byte_stream)
1842
1648
if response[0][0] not in ('ok', 'missing-basis'):
1843
1649
raise errors.UnexpectedSmartServerResponse(response)
1844
if self._last_substream is not None:
1845
# The stream included an inventory-delta record, but the remote
1846
# side isn't new enough to support them. So we need to send the
1847
# rest of the stream via VFS.
1848
self.target_repo.refresh_data()
1849
return self._resume_stream_with_vfs(response, src_format)
1850
1650
if response[0][0] == 'missing-basis':
1851
1651
tokens, missing_keys = bencode.bdecode_as_tuple(response[0][1])
1852
1652
resume_tokens = tokens
1855
1655
self.target_repo.refresh_data()
1856
1656
return [], set()
1858
def _resume_stream_with_vfs(self, response, src_format):
1859
"""Resume sending a stream via VFS, first resending the record and
1860
substream that couldn't be sent via an insert_stream verb.
1862
if response[0][0] == 'missing-basis':
1863
tokens, missing_keys = bencode.bdecode_as_tuple(response[0][1])
1864
# Ignore missing_keys, we haven't finished inserting yet
1867
def resume_substream():
1868
# Yield the substream that was interrupted.
1869
for record in self._last_substream:
1871
self._last_substream = None
1872
def resume_stream():
1873
# Finish sending the interrupted substream
1874
yield ('inventory-deltas', resume_substream())
1875
# Then simply continue sending the rest of the stream.
1876
for substream_kind, substream in self._last_stream:
1877
yield substream_kind, substream
1878
return self._insert_real(resume_stream(), src_format, tokens)
1880
def _stop_stream_if_inventory_delta(self, stream):
1881
"""Normally this just lets the original stream pass-through unchanged.
1883
However if any 'inventory-deltas' substream occurs it will stop
1884
streaming, and store the interrupted substream and stream in
1885
self._last_substream and self._last_stream so that the stream can be
1886
resumed by _resume_stream_with_vfs.
1889
stream_iter = iter(stream)
1890
for substream_kind, substream in stream_iter:
1891
if substream_kind == 'inventory-deltas':
1892
self._last_substream = substream
1893
self._last_stream = stream_iter
1896
yield substream_kind, substream
1899
1659
class RemoteStreamSource(repository.StreamSource):
1900
1660
"""Stream data from a remote server."""
1903
1663
if (self.from_repository._fallback_repositories and
1904
1664
self.to_format._fetch_order == 'topological'):
1905
1665
return self._real_stream(self.from_repository, search)
1908
repos = [self.from_repository]
1914
repos.extend(repo._fallback_repositories)
1915
sources.append(repo)
1916
return self.missing_parents_chain(search, sources)
1918
def get_stream_for_missing_keys(self, missing_keys):
1919
self.from_repository._ensure_real()
1920
real_repo = self.from_repository._real_repository
1921
real_source = real_repo._get_source(self.to_format)
1922
return real_source.get_stream_for_missing_keys(missing_keys)
1666
return self.missing_parents_chain(search, [self.from_repository] +
1667
self.from_repository._fallback_repositories)
1924
1669
def _real_stream(self, repo, search):
1925
1670
"""Get a stream for search from repo.
1956
1700
return self._real_stream(repo, search)
1957
1701
client = repo._client
1958
1702
medium = client._medium
1703
if medium._is_remote_before((1, 13)):
1704
# streaming was added in 1.13
1705
return self._real_stream(repo, search)
1959
1706
path = repo.bzrdir._path_for_remote_call(client)
1960
search_bytes = repo._serialise_search_result(search)
1961
args = (path, self.to_format.network_name())
1963
('Repository.get_stream_1.19', (1, 19)),
1964
('Repository.get_stream', (1, 13))]
1966
for verb, version in candidate_verbs:
1967
if medium._is_remote_before(version):
1970
response = repo._call_with_body_bytes_expecting_body(
1971
verb, args, search_bytes)
1972
except errors.UnknownSmartMethod:
1973
medium._remember_remote_is_before(version)
1975
response_tuple, response_handler = response
1708
search_bytes = repo._serialise_search_result(search)
1709
response = repo._call_with_body_bytes_expecting_body(
1710
'Repository.get_stream',
1711
(path, self.to_format.network_name()), search_bytes)
1712
response_tuple, response_handler = response
1713
except errors.UnknownSmartMethod:
1714
medium._remember_remote_is_before((1,13))
1979
1715
return self._real_stream(repo, search)
1980
1716
if response_tuple[0] != 'ok':
1981
1717
raise errors.UnexpectedSmartServerResponse(response_tuple)
1982
1718
byte_stream = response_handler.read_streamed_body()
1983
src_format, stream = smart_repo._byte_stream_to_stream(byte_stream,
1984
self._record_counter)
1719
src_format, stream = smart_repo._byte_stream_to_stream(byte_stream)
1985
1720
if src_format.network_name() != repo._format.network_name():
1986
1721
raise AssertionError(
1987
1722
"Mismatched RemoteRepository and stream src %r, %r" % (
2063
1797
self._network_name)
2065
1799
def get_format_description(self):
2067
return 'Remote: ' + self._custom_format.get_format_description()
1800
return 'Remote BZR Branch'
2069
1802
def network_name(self):
2070
1803
return self._network_name
2072
def open(self, a_bzrdir, name=None, ignore_fallbacks=False):
2073
return a_bzrdir.open_branch(name=name,
2074
ignore_fallbacks=ignore_fallbacks)
1805
def open(self, a_bzrdir, ignore_fallbacks=False):
1806
return a_bzrdir.open_branch(ignore_fallbacks=ignore_fallbacks)
2076
def _vfs_initialize(self, a_bzrdir, name):
1808
def _vfs_initialize(self, a_bzrdir):
2077
1809
# Initialisation when using a local bzrdir object, or a non-vfs init
2078
1810
# method is not available on the server.
2079
1811
# self._custom_format is always set - the start of initialize ensures
2081
1813
if isinstance(a_bzrdir, RemoteBzrDir):
2082
1814
a_bzrdir._ensure_real()
2083
result = self._custom_format.initialize(a_bzrdir._real_bzrdir,
1815
result = self._custom_format.initialize(a_bzrdir._real_bzrdir)
2086
1817
# We assume the bzrdir is parameterised; it may not be.
2087
result = self._custom_format.initialize(a_bzrdir, name)
1818
result = self._custom_format.initialize(a_bzrdir)
2088
1819
if (isinstance(a_bzrdir, RemoteBzrDir) and
2089
1820
not isinstance(result, RemoteBranch)):
2090
result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result,
1821
result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result)
2094
def initialize(self, a_bzrdir, name=None):
1824
def initialize(self, a_bzrdir):
2095
1825
# 1) get the network name to use.
2096
1826
if self._custom_format:
2097
1827
network_name = self._custom_format.network_name()
2103
1833
network_name = reference_format.network_name()
2104
1834
# Being asked to create on a non RemoteBzrDir:
2105
1835
if not isinstance(a_bzrdir, RemoteBzrDir):
2106
return self._vfs_initialize(a_bzrdir, name=name)
1836
return self._vfs_initialize(a_bzrdir)
2107
1837
medium = a_bzrdir._client._medium
2108
1838
if medium._is_remote_before((1, 13)):
2109
return self._vfs_initialize(a_bzrdir, name=name)
1839
return self._vfs_initialize(a_bzrdir)
2110
1840
# Creating on a remote bzr dir.
2111
1841
# 2) try direct creation via RPC
2112
1842
path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
2113
if name is not None:
2114
# XXX JRV20100304: Support creating colocated branches
2115
raise errors.NoColocatedBranchSupport(self)
2116
1843
verb = 'BzrDir.create_branch'
2118
1845
response = a_bzrdir._call(verb, path, network_name)
2119
1846
except errors.UnknownSmartMethod:
2120
1847
# Fallback - use vfs methods
2121
1848
medium._remember_remote_is_before((1, 13))
2122
return self._vfs_initialize(a_bzrdir, name=name)
1849
return self._vfs_initialize(a_bzrdir)
2123
1850
if response[0] != 'ok':
2124
1851
raise errors.UnexpectedSmartServerResponse(response)
2125
1852
# Turn the response into a RemoteRepository object.
2154
1881
self._ensure_real()
2155
1882
return self._custom_format.supports_stacking()
2157
def supports_set_append_revisions_only(self):
2159
return self._custom_format.supports_set_append_revisions_only()
2162
class RemoteBranch(branch.Branch, _RpcHelper, lock._RelockDebugMixin):
1885
class RemoteBranch(branch.Branch, _RpcHelper):
2163
1886
"""Branch stored on a server accessed by HPSS RPC.
2165
1888
At the moment most operations are mapped down to simple file operations.
2168
1891
def __init__(self, remote_bzrdir, remote_repository, real_branch=None,
2169
_client=None, format=None, setup_stacking=True, name=None):
1892
_client=None, format=None, setup_stacking=True):
2170
1893
"""Create a RemoteBranch instance.
2172
1895
:param real_branch: An optional local implementation of the branch
2178
1901
:param setup_stacking: If True make an RPC call to determine the
2179
1902
stacked (or not) status of the branch. If False assume the branch
2180
1903
is not stacked.
2181
:param name: Colocated branch name
2183
1905
# We intentionally don't call the parent class's __init__, because it
2184
1906
# will try to assign to self.tags, which is a property in this subclass.
2185
1907
# And the parent's __init__ doesn't do much anyway.
1908
self._revision_id_to_revno_cache = None
1909
self._partial_revision_id_to_revno_cache = {}
1910
self._revision_history_cache = None
1911
self._last_revision_info_cache = None
1912
self._merge_sorted_revisions_cache = None
2186
1913
self.bzrdir = remote_bzrdir
2187
1914
if _client is not None:
2188
1915
self._client = _client
2378
2089
return self._vfs_get_tags_bytes()
2379
2090
return response[0]
2381
def _vfs_set_tags_bytes(self, bytes):
2383
return self._real_branch._set_tags_bytes(bytes)
2385
def _set_tags_bytes(self, bytes):
2386
medium = self._client._medium
2387
if medium._is_remote_before((1, 18)):
2388
self._vfs_set_tags_bytes(bytes)
2392
self._remote_path(), self._lock_token, self._repo_lock_token)
2393
response = self._call_with_body_bytes(
2394
'Branch.set_tags_bytes', args, bytes)
2395
except errors.UnknownSmartMethod:
2396
medium._remember_remote_is_before((1, 18))
2397
self._vfs_set_tags_bytes(bytes)
2399
2092
def lock_read(self):
2400
"""Lock the branch for read operations.
2402
:return: A bzrlib.lock.LogicalLockResult.
2404
2093
self.repository.lock_read()
2405
2094
if not self._lock_mode:
2406
self._note_lock('r')
2407
2095
self._lock_mode = 'r'
2408
2096
self._lock_count = 1
2409
2097
if self._real_branch is not None:
2410
2098
self._real_branch.lock_read()
2412
2100
self._lock_count += 1
2413
return lock.LogicalLockResult(self.unlock)
2415
2102
def _remote_lock_write(self, token):
2416
2103
if token is None:
2417
2104
branch_token = repo_token = ''
2419
2106
branch_token = token
2420
repo_token = self.repository.lock_write().repository_token
2107
repo_token = self.repository.lock_write()
2421
2108
self.repository.unlock()
2422
2109
err_context = {'token': token}
2423
2110
response = self._call(
2517
2206
raise NotImplementedError(self.dont_leave_lock_in_place)
2518
2207
self._leave_lock = False
2521
def get_rev_id(self, revno, history=None):
2523
return _mod_revision.NULL_REVISION
2524
last_revision_info = self.last_revision_info()
2525
ok, result = self.repository.get_rev_id_for_revno(
2526
revno, last_revision_info)
2529
missing_parent = result[1]
2530
# Either the revision named by the server is missing, or its parent
2531
# is. Call get_parent_map to determine which, so that we report a
2533
parent_map = self.repository.get_parent_map([missing_parent])
2534
if missing_parent in parent_map:
2535
missing_parent = parent_map[missing_parent]
2536
raise errors.RevisionNotPresent(missing_parent, self.repository)
2538
2209
def _last_revision_info(self):
2539
2210
response = self._call('Branch.last_revision_info', self._remote_path())
2540
2211
if response[0] != 'ok':
2789
2457
medium = self._branch._client._medium
2790
2458
if medium._is_remote_before((1, 14)):
2791
2459
return self._vfs_set_option(value, name, section)
2792
if isinstance(value, dict):
2793
if medium._is_remote_before((2, 2)):
2794
return self._vfs_set_option(value, name, section)
2795
return self._set_config_option_dict(value, name, section)
2797
return self._set_config_option(value, name, section)
2799
def _set_config_option(self, value, name, section):
2801
2461
path = self._branch._remote_path()
2802
2462
response = self._branch._client.call('Branch.set_config_option',
2803
2463
path, self._branch._lock_token, self._branch._repo_lock_token,
2804
2464
value.encode('utf8'), name, section or '')
2805
2465
except errors.UnknownSmartMethod:
2806
medium = self._branch._client._medium
2807
2466
medium._remember_remote_is_before((1, 14))
2808
2467
return self._vfs_set_option(value, name, section)
2809
2468
if response != ():
2810
2469
raise errors.UnexpectedSmartServerResponse(response)
2812
def _serialize_option_dict(self, option_dict):
2814
for key, value in option_dict.items():
2815
if isinstance(key, unicode):
2816
key = key.encode('utf8')
2817
if isinstance(value, unicode):
2818
value = value.encode('utf8')
2819
utf8_dict[key] = value
2820
return bencode.bencode(utf8_dict)
2822
def _set_config_option_dict(self, value, name, section):
2824
path = self._branch._remote_path()
2825
serialised_dict = self._serialize_option_dict(value)
2826
response = self._branch._client.call(
2827
'Branch.set_config_option_dict',
2828
path, self._branch._lock_token, self._branch._repo_lock_token,
2829
serialised_dict, name, section or '')
2830
except errors.UnknownSmartMethod:
2831
medium = self._branch._client._medium
2832
medium._remember_remote_is_before((2, 2))
2833
return self._vfs_set_option(value, name, section)
2835
raise errors.UnexpectedSmartServerResponse(response)
2837
2471
def _real_object(self):
2838
2472
self._branch._ensure_real()
2839
2473
return self._branch._real_branch
2922
2556
'Missing key %r in context %r', key_err.args[0], context)
2925
if err.error_verb == 'IncompatibleRepositories':
2926
raise errors.IncompatibleRepositories(err.error_args[0],
2927
err.error_args[1], err.error_args[2])
2928
elif err.error_verb == 'NoSuchRevision':
2559
if err.error_verb == 'NoSuchRevision':
2929
2560
raise NoSuchRevision(find('branch'), err.error_args[0])
2930
2561
elif err.error_verb == 'nosuchrevision':
2931
2562
raise NoSuchRevision(find('repository'), err.error_args[0])
2932
elif err.error_verb == 'nobranch':
2933
if len(err.error_args) >= 1:
2934
extra = err.error_args[0]
2937
raise errors.NotBranchError(path=find('bzrdir').root_transport.base,
2563
elif err.error_tuple == ('nobranch',):
2564
raise errors.NotBranchError(path=find('bzrdir').root_transport.base)
2939
2565
elif err.error_verb == 'norepository':
2940
2566
raise errors.NoRepositoryPresent(find('bzrdir'))
2941
2567
elif err.error_verb == 'LockContention':