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
761
675
return self._real_repository.get_missing_parent_inventories(
762
676
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)
795
678
def _ensure_real(self):
796
679
"""Ensure that there is a _real_repository set.
896
779
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.edge.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
782
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
783
return (self.__class__ is other.__class__ and
917
784
self.bzrdir.transport.base == other.bzrdir.transport.base)
921
788
parents_provider = self._make_parents_provider(other_repository)
922
789
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
791
def gather_stats(self, revid=None, committers=None):
934
792
"""See Repository.gather_stats()."""
935
793
path = self.bzrdir._path_for_remote_call(self._client)
1249
1094
# state, so always add a lock here. If a caller passes us a locked
1250
1095
# repository, they are responsible for unlocking it later.
1251
1096
repository.lock_read()
1252
self._check_fallback_repository(repository)
1253
1097
self._fallback_repositories.append(repository)
1254
1098
# If self._real_repository was parameterised already (e.g. because a
1255
1099
# _real_branch had its get_stacked_on_url method called), then the
1256
1100
# repository to be added may already be in the _real_repositories list.
1257
1101
if self._real_repository is not None:
1258
fallback_locations = [repo.user_url for repo in
1102
fallback_locations = [repo.bzrdir.root_transport.base for repo in
1259
1103
self._real_repository._fallback_repositories]
1260
if repository.user_url not in fallback_locations:
1104
if repository.bzrdir.root_transport.base not in fallback_locations:
1261
1105
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
1107
def add_inventory(self, revid, inv, parents):
1274
1108
self._ensure_real()
1275
1109
return self._real_repository.add_inventory(revid, inv, parents)
1277
1111
def add_inventory_by_delta(self, basis_revision_id, delta, new_revision_id,
1278
parents, basis_inv=None, propagate_caches=False):
1279
1113
self._ensure_real()
1280
1114
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)
1115
delta, new_revision_id, parents)
1284
1117
def add_revision(self, rev_id, rev, inv=None, config=None):
1285
1118
self._ensure_real()
1315
1148
return self._real_repository.make_working_trees()
1317
1150
def refresh_data(self):
1318
"""Re-read any data needed to synchronise with disk.
1151
"""Re-read any data needed to to synchronise with disk.
1320
1153
This method is intended to be called after another repository instance
1321
1154
(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.
1155
repository. It may not be called during a write group, but may be
1156
called at any other time.
1158
if self.is_in_write_group():
1159
raise errors.InternalBzrError(
1160
"May not refresh_data while in a write group.")
1328
1161
if self._real_repository is not None:
1329
1162
self._real_repository.refresh_data()
1791
1624
def insert_stream(self, stream, src_format, resume_tokens):
1792
1625
target = self.target_repo
1793
1626
target._unstacked_provider.missing_keys.clear()
1794
candidate_calls = [('Repository.insert_stream_1.19', (1, 19))]
1795
1627
if target._lock_token:
1796
candidate_calls.append(('Repository.insert_stream_locked', (1, 14)))
1797
lock_args = (target._lock_token or '',)
1628
verb = 'Repository.insert_stream_locked'
1629
extra_args = (target._lock_token or '',)
1630
required_version = (1, 14)
1799
candidate_calls.append(('Repository.insert_stream', (1, 13)))
1632
verb = 'Repository.insert_stream'
1634
required_version = (1, 13)
1801
1635
client = target._client
1802
1636
medium = client._medium
1637
if medium._is_remote_before(required_version):
1638
# No possible way this can work.
1639
return self._insert_real(stream, src_format, resume_tokens)
1803
1640
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.
1641
if not resume_tokens:
1642
# XXX: Ugly but important for correctness, *will* be fixed during
1643
# 1.13 cycle. Pushing a stream that is interrupted results in a
1644
# fallback to the _real_repositories sink *with a partial stream*.
1645
# Thats bad because we insert less data than bzr expected. To avoid
1646
# this we do a trial push to make sure the verb is accessible, and
1647
# do not fallback when actually pushing the stream. A cleanup patch
1648
# is going to look at rewinding/restarting the stream/partial
1817
1650
byte_stream = smart_repo._stream_to_byte_stream([], src_format)
1819
1652
response = client.call_with_body_stream(
1820
(verb, path, '') + lock_args, byte_stream)
1653
(verb, path, '') + extra_args, byte_stream)
1821
1654
except errors.UnknownSmartMethod:
1822
1655
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)
1656
return self._insert_real(stream, src_format, resume_tokens)
1837
1657
byte_stream = smart_repo._stream_to_byte_stream(
1838
1658
stream, src_format)
1839
1659
resume_tokens = ' '.join(resume_tokens)
1840
1660
response = client.call_with_body_stream(
1841
(verb, path, resume_tokens) + lock_args, byte_stream)
1661
(verb, path, resume_tokens) + extra_args, byte_stream)
1842
1662
if response[0][0] not in ('ok', 'missing-basis'):
1843
1663
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
1664
if response[0][0] == 'missing-basis':
1851
1665
tokens, missing_keys = bencode.bdecode_as_tuple(response[0][1])
1852
1666
resume_tokens = tokens
1855
1669
self.target_repo.refresh_data()
1856
1670
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
1673
class RemoteStreamSource(repository.StreamSource):
1900
1674
"""Stream data from a remote server."""
1903
1677
if (self.from_repository._fallback_repositories and
1904
1678
self.to_format._fetch_order == 'topological'):
1905
1679
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)
1680
return self.missing_parents_chain(search, [self.from_repository] +
1681
self.from_repository._fallback_repositories)
1924
1683
def _real_stream(self, repo, search):
1925
1684
"""Get a stream for search from repo.
1956
1714
return self._real_stream(repo, search)
1957
1715
client = repo._client
1958
1716
medium = client._medium
1717
if medium._is_remote_before((1, 13)):
1718
# streaming was added in 1.13
1719
return self._real_stream(repo, search)
1959
1720
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
1722
search_bytes = repo._serialise_search_result(search)
1723
response = repo._call_with_body_bytes_expecting_body(
1724
'Repository.get_stream',
1725
(path, self.to_format.network_name()), search_bytes)
1726
response_tuple, response_handler = response
1727
except errors.UnknownSmartMethod:
1728
medium._remember_remote_is_before((1,13))
1979
1729
return self._real_stream(repo, search)
1980
1730
if response_tuple[0] != 'ok':
1981
1731
raise errors.UnexpectedSmartServerResponse(response_tuple)
2062
1811
self._network_name)
2064
1813
def get_format_description(self):
2066
return 'Remote: ' + self._custom_format.get_format_description()
1814
return 'Remote BZR Branch'
2068
1816
def network_name(self):
2069
1817
return self._network_name
2071
def open(self, a_bzrdir, name=None, ignore_fallbacks=False):
2072
return a_bzrdir.open_branch(name=name,
2073
ignore_fallbacks=ignore_fallbacks)
1819
def open(self, a_bzrdir, ignore_fallbacks=False):
1820
return a_bzrdir.open_branch(ignore_fallbacks=ignore_fallbacks)
2075
def _vfs_initialize(self, a_bzrdir, name):
1822
def _vfs_initialize(self, a_bzrdir):
2076
1823
# Initialisation when using a local bzrdir object, or a non-vfs init
2077
1824
# method is not available on the server.
2078
1825
# self._custom_format is always set - the start of initialize ensures
2080
1827
if isinstance(a_bzrdir, RemoteBzrDir):
2081
1828
a_bzrdir._ensure_real()
2082
result = self._custom_format.initialize(a_bzrdir._real_bzrdir,
1829
result = self._custom_format.initialize(a_bzrdir._real_bzrdir)
2085
1831
# We assume the bzrdir is parameterised; it may not be.
2086
result = self._custom_format.initialize(a_bzrdir, name)
1832
result = self._custom_format.initialize(a_bzrdir)
2087
1833
if (isinstance(a_bzrdir, RemoteBzrDir) and
2088
1834
not isinstance(result, RemoteBranch)):
2089
result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result,
1835
result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result)
2093
def initialize(self, a_bzrdir, name=None):
1838
def initialize(self, a_bzrdir):
2094
1839
# 1) get the network name to use.
2095
1840
if self._custom_format:
2096
1841
network_name = self._custom_format.network_name()
2102
1847
network_name = reference_format.network_name()
2103
1848
# Being asked to create on a non RemoteBzrDir:
2104
1849
if not isinstance(a_bzrdir, RemoteBzrDir):
2105
return self._vfs_initialize(a_bzrdir, name=name)
1850
return self._vfs_initialize(a_bzrdir)
2106
1851
medium = a_bzrdir._client._medium
2107
1852
if medium._is_remote_before((1, 13)):
2108
return self._vfs_initialize(a_bzrdir, name=name)
1853
return self._vfs_initialize(a_bzrdir)
2109
1854
# Creating on a remote bzr dir.
2110
1855
# 2) try direct creation via RPC
2111
1856
path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
2112
if name is not None:
2113
# XXX JRV20100304: Support creating colocated branches
2114
raise errors.NoColocatedBranchSupport(self)
2115
1857
verb = 'BzrDir.create_branch'
2117
1859
response = a_bzrdir._call(verb, path, network_name)
2118
1860
except errors.UnknownSmartMethod:
2119
1861
# Fallback - use vfs methods
2120
1862
medium._remember_remote_is_before((1, 13))
2121
return self._vfs_initialize(a_bzrdir, name=name)
1863
return self._vfs_initialize(a_bzrdir)
2122
1864
if response[0] != 'ok':
2123
1865
raise errors.UnexpectedSmartServerResponse(response)
2124
1866
# Turn the response into a RemoteRepository object.
2153
1895
self._ensure_real()
2154
1896
return self._custom_format.supports_stacking()
2156
def supports_set_append_revisions_only(self):
2158
return self._custom_format.supports_set_append_revisions_only()
2161
class RemoteBranch(branch.Branch, _RpcHelper, lock._RelockDebugMixin):
1899
class RemoteBranch(branch.Branch, _RpcHelper):
2162
1900
"""Branch stored on a server accessed by HPSS RPC.
2164
1902
At the moment most operations are mapped down to simple file operations.
2167
1905
def __init__(self, remote_bzrdir, remote_repository, real_branch=None,
2168
_client=None, format=None, setup_stacking=True, name=None):
1906
_client=None, format=None, setup_stacking=True):
2169
1907
"""Create a RemoteBranch instance.
2171
1909
:param real_branch: An optional local implementation of the branch
2177
1915
:param setup_stacking: If True make an RPC call to determine the
2178
1916
stacked (or not) status of the branch. If False assume the branch
2179
1917
is not stacked.
2180
:param name: Colocated branch name
2182
1919
# We intentionally don't call the parent class's __init__, because it
2183
1920
# will try to assign to self.tags, which is a property in this subclass.
2184
1921
# And the parent's __init__ doesn't do much anyway.
1922
self._revision_id_to_revno_cache = None
1923
self._partial_revision_id_to_revno_cache = {}
1924
self._revision_history_cache = None
1925
self._last_revision_info_cache = None
1926
self._merge_sorted_revisions_cache = None
2185
1927
self.bzrdir = remote_bzrdir
2186
1928
if _client is not None:
2187
1929
self._client = _client
2377
2103
return self._vfs_get_tags_bytes()
2378
2104
return response[0]
2380
def _vfs_set_tags_bytes(self, bytes):
2382
return self._real_branch._set_tags_bytes(bytes)
2384
def _set_tags_bytes(self, bytes):
2385
medium = self._client._medium
2386
if medium._is_remote_before((1, 18)):
2387
self._vfs_set_tags_bytes(bytes)
2391
self._remote_path(), self._lock_token, self._repo_lock_token)
2392
response = self._call_with_body_bytes(
2393
'Branch.set_tags_bytes', args, bytes)
2394
except errors.UnknownSmartMethod:
2395
medium._remember_remote_is_before((1, 18))
2396
self._vfs_set_tags_bytes(bytes)
2398
2106
def lock_read(self):
2399
"""Lock the branch for read operations.
2401
:return: A bzrlib.lock.LogicalLockResult.
2403
2107
self.repository.lock_read()
2404
2108
if not self._lock_mode:
2405
self._note_lock('r')
2406
2109
self._lock_mode = 'r'
2407
2110
self._lock_count = 1
2408
2111
if self._real_branch is not None:
2409
2112
self._real_branch.lock_read()
2411
2114
self._lock_count += 1
2412
return lock.LogicalLockResult(self.unlock)
2414
2116
def _remote_lock_write(self, token):
2415
2117
if token is None:
2416
2118
branch_token = repo_token = ''
2418
2120
branch_token = token
2419
repo_token = self.repository.lock_write().repository_token
2121
repo_token = self.repository.lock_write()
2420
2122
self.repository.unlock()
2421
2123
err_context = {'token': token}
2422
2124
response = self._call(
2516
2220
raise NotImplementedError(self.dont_leave_lock_in_place)
2517
2221
self._leave_lock = False
2520
def get_rev_id(self, revno, history=None):
2522
return _mod_revision.NULL_REVISION
2523
last_revision_info = self.last_revision_info()
2524
ok, result = self.repository.get_rev_id_for_revno(
2525
revno, last_revision_info)
2528
missing_parent = result[1]
2529
# Either the revision named by the server is missing, or its parent
2530
# is. Call get_parent_map to determine which, so that we report a
2532
parent_map = self.repository.get_parent_map([missing_parent])
2533
if missing_parent in parent_map:
2534
missing_parent = parent_map[missing_parent]
2535
raise errors.RevisionNotPresent(missing_parent, self.repository)
2537
2223
def _last_revision_info(self):
2538
2224
response = self._call('Branch.last_revision_info', self._remote_path())
2539
2225
if response[0] != 'ok':
2887
2570
'Missing key %r in context %r', key_err.args[0], context)
2890
if err.error_verb == 'IncompatibleRepositories':
2891
raise errors.IncompatibleRepositories(err.error_args[0],
2892
err.error_args[1], err.error_args[2])
2893
elif err.error_verb == 'NoSuchRevision':
2573
if err.error_verb == 'NoSuchRevision':
2894
2574
raise NoSuchRevision(find('branch'), err.error_args[0])
2895
2575
elif err.error_verb == 'nosuchrevision':
2896
2576
raise NoSuchRevision(find('repository'), err.error_args[0])
2897
elif err.error_verb == 'nobranch':
2898
if len(err.error_args) >= 1:
2899
extra = err.error_args[0]
2902
raise errors.NotBranchError(path=find('bzrdir').root_transport.base,
2577
elif err.error_tuple == ('nobranch',):
2578
raise errors.NotBranchError(path=find('bzrdir').root_transport.base)
2904
2579
elif err.error_verb == 'norepository':
2905
2580
raise errors.NoRepositoryPresent(find('bzrdir'))
2906
2581
elif err.error_verb == 'LockContention':