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 (
31
repository as _mod_repository,
33
revision as _mod_revision,
37
from bzrlib.branch import BranchReferenceFormat, BranchWriteLockResult
36
from bzrlib.branch import BranchReferenceFormat
38
37
from bzrlib.bzrdir import BzrDir, RemoteBzrDirFormat
39
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
38
from bzrlib.decorators import needs_read_lock, needs_write_lock
40
39
from bzrlib.errors import (
42
41
SmartProtocolError,
63
62
except errors.ErrorFromSmartServer, err:
64
63
self._translate_error(err, **err_context)
66
def _call_with_body_bytes(self, method, args, body_bytes, **err_context):
68
return self._client.call_with_body_bytes(method, args, body_bytes)
69
except errors.ErrorFromSmartServer, err:
70
self._translate_error(err, **err_context)
72
65
def _call_with_body_bytes_expecting_body(self, method, args, body_bytes,
113
105
self._client = client._SmartClient(medium)
115
107
self._client = _client
122
return '%s(%r)' % (self.__class__.__name__, self._client)
124
def _probe_bzrdir(self):
125
medium = self._client._medium
126
110
path = self._path_for_remote_call(self._client)
127
if medium._is_remote_before((2, 1)):
131
self._rpc_open_2_1(path)
133
except errors.UnknownSmartMethod:
134
medium._remember_remote_is_before((2, 1))
137
def _rpc_open_2_1(self, path):
138
response = self._call('BzrDir.open_2.1', path)
139
if response == ('no',):
140
raise errors.NotBranchError(path=self.root_transport.base)
141
elif response[0] == 'yes':
142
if response[1] == 'yes':
143
self._has_working_tree = True
144
elif response[1] == 'no':
145
self._has_working_tree = False
147
raise errors.UnexpectedSmartServerResponse(response)
149
raise errors.UnexpectedSmartServerResponse(response)
151
def _rpc_open(self, path):
152
111
response = self._call('BzrDir.open', path)
153
112
if response not in [('yes',), ('no',)]:
154
113
raise errors.UnexpectedSmartServerResponse(response)
155
114
if response == ('no',):
156
raise errors.NotBranchError(path=self.root_transport.base)
115
raise errors.NotBranchError(path=transport.base)
158
117
def _ensure_real(self):
159
118
"""Ensure that there is a _real_bzrdir set.
246
201
self._ensure_real()
247
202
self._real_bzrdir.destroy_repository()
249
def create_branch(self, name=None):
204
def create_branch(self):
250
205
# as per meta1 formats - just delegate to the format object which may
251
206
# be parameterised.
252
real_branch = self._format.get_branch_format().initialize(self,
207
real_branch = self._format.get_branch_format().initialize(self)
254
208
if not isinstance(real_branch, RemoteBranch):
255
result = RemoteBranch(self, self.find_repository(), real_branch,
209
result = RemoteBranch(self, self.find_repository(), real_branch)
258
211
result = real_branch
259
212
# BzrDir.clone_on_transport() uses the result of create_branch but does
265
218
self._next_open_branch_result = result
268
def destroy_branch(self, name=None):
221
def destroy_branch(self):
269
222
"""See BzrDir.destroy_branch"""
270
223
self._ensure_real()
271
self._real_bzrdir.destroy_branch(name=name)
224
self._real_bzrdir.destroy_branch()
272
225
self._next_open_branch_result = None
274
227
def create_workingtree(self, revision_id=None, from_branch=None):
275
228
raise errors.NotLocalUrl(self.transport.base)
277
def find_branch_format(self, name=None):
230
def find_branch_format(self):
278
231
"""Find the branch 'format' for this bzrdir.
280
233
This might be a synthetic object for e.g. RemoteBranch and SVN.
282
b = self.open_branch(name=name)
235
b = self.open_branch()
285
def get_branch_reference(self, name=None):
238
def get_branch_reference(self):
286
239
"""See BzrDir.get_branch_reference()."""
288
# XXX JRV20100304: Support opening colocated branches
289
raise errors.NoColocatedBranchSupport(self)
290
240
response = self._get_branch_reference()
291
241
if response[0] == 'ref':
292
242
return response[1]
296
246
def _get_branch_reference(self):
297
247
path = self._path_for_remote_call(self._client)
298
248
medium = self._client._medium
300
('BzrDir.open_branchV3', (2, 1)),
301
('BzrDir.open_branchV2', (1, 13)),
302
('BzrDir.open_branch', None),
304
for verb, required_version in candidate_calls:
305
if required_version and medium._is_remote_before(required_version):
249
if not medium._is_remote_before((1, 13)):
308
response = self._call(verb, path)
251
response = self._call('BzrDir.open_branchV2', path)
252
if response[0] not in ('ref', 'branch'):
253
raise errors.UnexpectedSmartServerResponse(response)
309
255
except errors.UnknownSmartMethod:
310
if required_version is None:
312
medium._remember_remote_is_before(required_version)
315
if verb == 'BzrDir.open_branch':
316
if response[0] != 'ok':
317
raise errors.UnexpectedSmartServerResponse(response)
318
if response[1] != '':
319
return ('ref', response[1])
321
return ('branch', '')
322
if response[0] not in ('ref', 'branch'):
256
medium._remember_remote_is_before((1, 13))
257
response = self._call('BzrDir.open_branch', path)
258
if response[0] != 'ok':
323
259
raise errors.UnexpectedSmartServerResponse(response)
260
if response[1] != '':
261
return ('ref', response[1])
263
return ('branch', '')
326
def _get_tree_branch(self, name=None):
265
def _get_tree_branch(self):
327
266
"""See BzrDir._get_tree_branch()."""
328
return None, self.open_branch(name=name)
267
return None, self.open_branch()
330
def open_branch(self, name=None, unsupported=False,
331
ignore_fallbacks=False):
269
def open_branch(self, _unsupported=False, ignore_fallbacks=False):
333
271
raise NotImplementedError('unsupported flag support not implemented yet.')
334
272
if self._next_open_branch_result is not None:
335
273
# See create_branch for details.
340
278
if response[0] == 'ref':
341
279
# a branch reference, use the existing BranchReference logic.
342
280
format = BranchReferenceFormat()
343
return format.open(self, name=name, _found=True,
344
location=response[1], ignore_fallbacks=ignore_fallbacks)
281
return format.open(self, _found=True, location=response[1],
282
ignore_fallbacks=ignore_fallbacks)
345
283
branch_format_name = response[1]
346
284
if not branch_format_name:
347
285
branch_format_name = None
348
286
format = RemoteBranchFormat(network_name=branch_format_name)
349
287
return RemoteBranch(self, self.find_repository(), format=format,
350
setup_stacking=not ignore_fallbacks, name=name)
288
setup_stacking=not ignore_fallbacks)
352
290
def _open_repo_v1(self, path):
353
291
verb = 'BzrDir.find_repository'
625
546
return self._custom_format._fetch_reconcile
627
548
def get_format_description(self):
629
return 'Remote: ' + self._custom_format.get_format_description()
549
return 'bzr remote repository'
631
551
def __eq__(self, other):
632
552
return self.__class__ is other.__class__
554
def check_conversion_target(self, target_format):
555
if self.rich_root_data and not target_format.rich_root_data:
556
raise errors.BadConversionTarget(
557
'Does not support rich root data.', target_format)
558
if (self.supports_tree_reference and
559
not getattr(target_format, 'supports_tree_reference', False)):
560
raise errors.BadConversionTarget(
561
'Does not support nested trees', target_format)
634
563
def network_name(self):
635
564
if self._network_name:
636
565
return self._network_name
757
667
self._ensure_real()
758
668
return self._real_repository.suspend_write_group()
760
def get_missing_parent_inventories(self, check_for_missing_texts=True):
762
return self._real_repository.get_missing_parent_inventories(
763
check_for_missing_texts=check_for_missing_texts)
765
def _get_rev_id_for_revno_vfs(self, revno, known_pair):
767
return self._real_repository.get_rev_id_for_revno(
770
def get_rev_id_for_revno(self, revno, known_pair):
771
"""See Repository.get_rev_id_for_revno."""
772
path = self.bzrdir._path_for_remote_call(self._client)
774
if self._client._medium._is_remote_before((1, 17)):
775
return self._get_rev_id_for_revno_vfs(revno, known_pair)
776
response = self._call(
777
'Repository.get_rev_id_for_revno', path, revno, known_pair)
778
except errors.UnknownSmartMethod:
779
self._client._medium._remember_remote_is_before((1, 17))
780
return self._get_rev_id_for_revno_vfs(revno, known_pair)
781
if response[0] == 'ok':
782
return True, response[1]
783
elif response[0] == 'history-incomplete':
784
known_pair = response[1:3]
785
for fallback in self._fallback_repositories:
786
found, result = fallback.get_rev_id_for_revno(revno, known_pair)
791
# Not found in any fallbacks
792
return False, known_pair
794
raise errors.UnexpectedSmartServerResponse(response)
670
def get_missing_parent_inventories(self):
672
return self._real_repository.get_missing_parent_inventories()
796
674
def _ensure_real(self):
797
675
"""Ensure that there is a _real_repository set.
877
750
"""Return a source for streaming from this repository."""
878
751
return RemoteStreamSource(self, to_format)
881
753
def has_revision(self, revision_id):
882
"""True if this repository has a copy of the revision."""
883
# Copy of bzrlib.repository.Repository.has_revision
884
return revision_id in self.has_revisions((revision_id,))
754
"""See Repository.has_revision()."""
755
if revision_id == NULL_REVISION:
756
# The null revision is always present.
758
path = self.bzrdir._path_for_remote_call(self._client)
759
response = self._call('Repository.has_revision', path, revision_id)
760
if response[0] not in ('yes', 'no'):
761
raise errors.UnexpectedSmartServerResponse(response)
762
if response[0] == 'yes':
764
for fallback_repo in self._fallback_repositories:
765
if fallback_repo.has_revision(revision_id):
887
769
def has_revisions(self, revision_ids):
888
"""Probe to find out the presence of multiple revisions.
890
:param revision_ids: An iterable of revision_ids.
891
:return: A set of the revision_ids that were present.
893
# Copy of bzrlib.repository.Repository.has_revisions
894
parent_map = self.get_parent_map(revision_ids)
895
result = set(parent_map)
896
if _mod_revision.NULL_REVISION in revision_ids:
897
result.add(_mod_revision.NULL_REVISION)
770
"""See Repository.has_revisions()."""
771
# FIXME: This does many roundtrips, particularly when there are
772
# fallback repositories. -- mbp 20080905
774
for revision_id in revision_ids:
775
if self.has_revision(revision_id):
776
result.add(revision_id)
900
def _has_same_fallbacks(self, other_repo):
901
"""Returns true if the repositories have the same fallbacks."""
902
# XXX: copied from Repository; it should be unified into a base class
903
# <https://bugs.launchpad.net/bzr/+bug/401622>
904
my_fb = self._fallback_repositories
905
other_fb = other_repo._fallback_repositories
906
if len(my_fb) != len(other_fb):
908
for f, g in zip(my_fb, other_fb):
909
if not f.has_same_location(g):
913
779
def has_same_location(self, other):
914
# TODO: Move to RepositoryBase and unify with the regular Repository
915
# one; unfortunately the tests rely on slightly different behaviour at
916
# present -- mbp 20090710
917
780
return (self.__class__ is other.__class__ and
918
781
self.bzrdir.transport.base == other.bzrdir.transport.base)
922
785
parents_provider = self._make_parents_provider(other_repository)
923
786
return graph.Graph(parents_provider)
926
def get_known_graph_ancestry(self, revision_ids):
927
"""Return the known graph for a set of revision ids and their ancestors.
929
st = static_tuple.StaticTuple
930
revision_keys = [st(r_id).intern() for r_id in revision_ids]
931
known_graph = self.revisions.get_known_graph_ancestry(revision_keys)
932
return graph.GraphThunkIdsToKeys(known_graph)
934
788
def gather_stats(self, revid=None, committers=None):
935
789
"""See Repository.gather_stats()."""
936
790
path = self.bzrdir._path_for_remote_call(self._client)
996
850
def is_write_locked(self):
997
851
return self._lock_mode == 'w'
999
def _warn_if_deprecated(self, branch=None):
1000
# If we have a real repository, the check will be done there, if we
1001
# don't the check will be done remotely.
1004
853
def lock_read(self):
1005
"""Lock the repository for read operations.
1007
:return: A bzrlib.lock.LogicalLockResult.
1009
854
# wrong eventually - want a local lock cache context
1010
855
if not self._lock_mode:
1011
self._note_lock('r')
1012
856
self._lock_mode = 'r'
1013
857
self._lock_count = 1
1014
858
self._unstacked_provider.enable_cache(cache_misses=True)
1015
859
if self._real_repository is not None:
1016
860
self._real_repository.lock_read()
1017
for repo in self._fallback_repositories:
1020
862
self._lock_count += 1
1021
return lock.LogicalLockResult(self.unlock)
863
for repo in self._fallback_repositories:
1023
866
def _remote_lock_write(self, token):
1024
867
path = self.bzrdir._path_for_remote_call(self._client)
1245
1081
# We need to accumulate additional repositories here, to pass them in
1246
1082
# on various RPC's.
1248
if self.is_locked():
1249
# We will call fallback.unlock() when we transition to the unlocked
1250
# state, so always add a lock here. If a caller passes us a locked
1251
# repository, they are responsible for unlocking it later.
1252
repository.lock_read()
1253
self._check_fallback_repository(repository)
1254
1084
self._fallback_repositories.append(repository)
1255
1085
# If self._real_repository was parameterised already (e.g. because a
1256
1086
# _real_branch had its get_stacked_on_url method called), then the
1257
1087
# repository to be added may already be in the _real_repositories list.
1258
1088
if self._real_repository is not None:
1259
fallback_locations = [repo.user_url for repo in
1089
fallback_locations = [repo.bzrdir.root_transport.base for repo in
1260
1090
self._real_repository._fallback_repositories]
1261
if repository.user_url not in fallback_locations:
1091
if repository.bzrdir.root_transport.base not in fallback_locations:
1262
1092
self._real_repository.add_fallback_repository(repository)
1264
def _check_fallback_repository(self, repository):
1265
"""Check that this repository can fallback to repository safely.
1267
Raise an error if not.
1269
:param repository: A repository to fallback to.
1271
return _mod_repository.InterRepository._assert_same_model(
1274
1094
def add_inventory(self, revid, inv, parents):
1275
1095
self._ensure_real()
1276
1096
return self._real_repository.add_inventory(revid, inv, parents)
1278
1098
def add_inventory_by_delta(self, basis_revision_id, delta, new_revision_id,
1279
parents, basis_inv=None, propagate_caches=False):
1280
1100
self._ensure_real()
1281
1101
return self._real_repository.add_inventory_by_delta(basis_revision_id,
1282
delta, new_revision_id, parents, basis_inv=basis_inv,
1283
propagate_caches=propagate_caches)
1102
delta, new_revision_id, parents)
1285
1104
def add_revision(self, rev_id, rev, inv=None, config=None):
1286
1105
self._ensure_real()
1316
1135
return self._real_repository.make_working_trees()
1318
1137
def refresh_data(self):
1319
"""Re-read any data needed to synchronise with disk.
1138
"""Re-read any data needed to to synchronise with disk.
1321
1140
This method is intended to be called after another repository instance
1322
1141
(such as one used by a smart server) has inserted data into the
1323
repository. On all repositories this will work outside of write groups.
1324
Some repository formats (pack and newer for bzrlib native formats)
1325
support refresh_data inside write groups. If called inside a write
1326
group on a repository that does not support refreshing in a write group
1327
IsInWriteGroupError will be raised.
1142
repository. It may not be called during a write group, but may be
1143
called at any other time.
1145
if self.is_in_write_group():
1146
raise errors.InternalBzrError(
1147
"May not refresh_data while in a write group.")
1329
1148
if self._real_repository is not None:
1330
1149
self._real_repository.refresh_data()
1792
1611
def insert_stream(self, stream, src_format, resume_tokens):
1793
1612
target = self.target_repo
1794
target._unstacked_provider.missing_keys.clear()
1795
candidate_calls = [('Repository.insert_stream_1.19', (1, 19))]
1796
1613
if target._lock_token:
1797
candidate_calls.append(('Repository.insert_stream_locked', (1, 14)))
1798
lock_args = (target._lock_token or '',)
1614
verb = 'Repository.insert_stream_locked'
1615
extra_args = (target._lock_token or '',)
1616
required_version = (1, 14)
1800
candidate_calls.append(('Repository.insert_stream', (1, 13)))
1618
verb = 'Repository.insert_stream'
1620
required_version = (1, 13)
1802
1621
client = target._client
1803
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)
1804
1626
path = target.bzrdir._path_for_remote_call(client)
1805
# Probe for the verb to use with an empty stream before sending the
1806
# real stream to it. We do this both to avoid the risk of sending a
1807
# large request that is then rejected, and because we don't want to
1808
# implement a way to buffer, rewind, or restart the stream.
1810
for verb, required_version in candidate_calls:
1811
if medium._is_remote_before(required_version):
1814
# We've already done the probing (and set _is_remote_before) on
1815
# 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
1818
1636
byte_stream = smart_repo._stream_to_byte_stream([], src_format)
1820
1638
response = client.call_with_body_stream(
1821
(verb, path, '') + lock_args, byte_stream)
1639
(verb, path, '') + extra_args, byte_stream)
1822
1640
except errors.UnknownSmartMethod:
1823
1641
medium._remember_remote_is_before(required_version)
1829
return self._insert_real(stream, src_format, resume_tokens)
1830
self._last_inv_record = None
1831
self._last_substream = None
1832
if required_version < (1, 19):
1833
# Remote side doesn't support inventory deltas. Wrap the stream to
1834
# make sure we don't send any. If the stream contains inventory
1835
# deltas we'll interrupt the smart insert_stream request and
1837
stream = self._stop_stream_if_inventory_delta(stream)
1642
return self._insert_real(stream, src_format, resume_tokens)
1838
1643
byte_stream = smart_repo._stream_to_byte_stream(
1839
1644
stream, src_format)
1840
1645
resume_tokens = ' '.join(resume_tokens)
1841
1646
response = client.call_with_body_stream(
1842
(verb, path, resume_tokens) + lock_args, byte_stream)
1647
(verb, path, resume_tokens) + extra_args, byte_stream)
1843
1648
if response[0][0] not in ('ok', 'missing-basis'):
1844
1649
raise errors.UnexpectedSmartServerResponse(response)
1845
if self._last_substream is not None:
1846
# The stream included an inventory-delta record, but the remote
1847
# side isn't new enough to support them. So we need to send the
1848
# rest of the stream via VFS.
1849
self.target_repo.refresh_data()
1850
return self._resume_stream_with_vfs(response, src_format)
1851
1650
if response[0][0] == 'missing-basis':
1852
1651
tokens, missing_keys = bencode.bdecode_as_tuple(response[0][1])
1853
1652
resume_tokens = tokens
1856
1655
self.target_repo.refresh_data()
1857
1656
return [], set()
1859
def _resume_stream_with_vfs(self, response, src_format):
1860
"""Resume sending a stream via VFS, first resending the record and
1861
substream that couldn't be sent via an insert_stream verb.
1863
if response[0][0] == 'missing-basis':
1864
tokens, missing_keys = bencode.bdecode_as_tuple(response[0][1])
1865
# Ignore missing_keys, we haven't finished inserting yet
1868
def resume_substream():
1869
# Yield the substream that was interrupted.
1870
for record in self._last_substream:
1872
self._last_substream = None
1873
def resume_stream():
1874
# Finish sending the interrupted substream
1875
yield ('inventory-deltas', resume_substream())
1876
# Then simply continue sending the rest of the stream.
1877
for substream_kind, substream in self._last_stream:
1878
yield substream_kind, substream
1879
return self._insert_real(resume_stream(), src_format, tokens)
1881
def _stop_stream_if_inventory_delta(self, stream):
1882
"""Normally this just lets the original stream pass-through unchanged.
1884
However if any 'inventory-deltas' substream occurs it will stop
1885
streaming, and store the interrupted substream and stream in
1886
self._last_substream and self._last_stream so that the stream can be
1887
resumed by _resume_stream_with_vfs.
1890
stream_iter = iter(stream)
1891
for substream_kind, substream in stream_iter:
1892
if substream_kind == 'inventory-deltas':
1893
self._last_substream = substream
1894
self._last_stream = stream_iter
1897
yield substream_kind, substream
1900
1659
class RemoteStreamSource(repository.StreamSource):
1901
1660
"""Stream data from a remote server."""
1904
1663
if (self.from_repository._fallback_repositories and
1905
1664
self.to_format._fetch_order == 'topological'):
1906
1665
return self._real_stream(self.from_repository, search)
1909
repos = [self.from_repository]
1915
repos.extend(repo._fallback_repositories)
1916
sources.append(repo)
1917
return self.missing_parents_chain(search, sources)
1919
def get_stream_for_missing_keys(self, missing_keys):
1920
self.from_repository._ensure_real()
1921
real_repo = self.from_repository._real_repository
1922
real_source = real_repo._get_source(self.to_format)
1923
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)
1925
1669
def _real_stream(self, repo, search):
1926
1670
"""Get a stream for search from repo.
1957
1700
return self._real_stream(repo, search)
1958
1701
client = repo._client
1959
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)
1960
1706
path = repo.bzrdir._path_for_remote_call(client)
1961
search_bytes = repo._serialise_search_result(search)
1962
args = (path, self.to_format.network_name())
1964
('Repository.get_stream_1.19', (1, 19)),
1965
('Repository.get_stream', (1, 13))]
1967
for verb, version in candidate_verbs:
1968
if medium._is_remote_before(version):
1971
response = repo._call_with_body_bytes_expecting_body(
1972
verb, args, search_bytes)
1973
except errors.UnknownSmartMethod:
1974
medium._remember_remote_is_before(version)
1976
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))
1980
1715
return self._real_stream(repo, search)
1981
1716
if response_tuple[0] != 'ok':
1982
1717
raise errors.UnexpectedSmartServerResponse(response_tuple)
1983
1718
byte_stream = response_handler.read_streamed_body()
1984
src_format, stream = smart_repo._byte_stream_to_stream(byte_stream,
1985
self._record_counter)
1719
src_format, stream = smart_repo._byte_stream_to_stream(byte_stream)
1986
1720
if src_format.network_name() != repo._format.network_name():
1987
1721
raise AssertionError(
1988
1722
"Mismatched RemoteRepository and stream src %r, %r" % (
2064
1797
self._network_name)
2066
1799
def get_format_description(self):
2068
return 'Remote: ' + self._custom_format.get_format_description()
1800
return 'Remote BZR Branch'
2070
1802
def network_name(self):
2071
1803
return self._network_name
2073
def open(self, a_bzrdir, name=None, ignore_fallbacks=False):
2074
return a_bzrdir.open_branch(name=name,
2075
ignore_fallbacks=ignore_fallbacks)
1805
def open(self, a_bzrdir, ignore_fallbacks=False):
1806
return a_bzrdir.open_branch(ignore_fallbacks=ignore_fallbacks)
2077
def _vfs_initialize(self, a_bzrdir, name):
1808
def _vfs_initialize(self, a_bzrdir):
2078
1809
# Initialisation when using a local bzrdir object, or a non-vfs init
2079
1810
# method is not available on the server.
2080
1811
# self._custom_format is always set - the start of initialize ensures
2082
1813
if isinstance(a_bzrdir, RemoteBzrDir):
2083
1814
a_bzrdir._ensure_real()
2084
result = self._custom_format.initialize(a_bzrdir._real_bzrdir,
1815
result = self._custom_format.initialize(a_bzrdir._real_bzrdir)
2087
1817
# We assume the bzrdir is parameterised; it may not be.
2088
result = self._custom_format.initialize(a_bzrdir, name)
1818
result = self._custom_format.initialize(a_bzrdir)
2089
1819
if (isinstance(a_bzrdir, RemoteBzrDir) and
2090
1820
not isinstance(result, RemoteBranch)):
2091
result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result,
1821
result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result)
2095
def initialize(self, a_bzrdir, name=None):
1824
def initialize(self, a_bzrdir):
2096
1825
# 1) get the network name to use.
2097
1826
if self._custom_format:
2098
1827
network_name = self._custom_format.network_name()
2104
1833
network_name = reference_format.network_name()
2105
1834
# Being asked to create on a non RemoteBzrDir:
2106
1835
if not isinstance(a_bzrdir, RemoteBzrDir):
2107
return self._vfs_initialize(a_bzrdir, name=name)
1836
return self._vfs_initialize(a_bzrdir)
2108
1837
medium = a_bzrdir._client._medium
2109
1838
if medium._is_remote_before((1, 13)):
2110
return self._vfs_initialize(a_bzrdir, name=name)
1839
return self._vfs_initialize(a_bzrdir)
2111
1840
# Creating on a remote bzr dir.
2112
1841
# 2) try direct creation via RPC
2113
1842
path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
2114
if name is not None:
2115
# XXX JRV20100304: Support creating colocated branches
2116
raise errors.NoColocatedBranchSupport(self)
2117
1843
verb = 'BzrDir.create_branch'
2119
1845
response = a_bzrdir._call(verb, path, network_name)
2120
1846
except errors.UnknownSmartMethod:
2121
1847
# Fallback - use vfs methods
2122
1848
medium._remember_remote_is_before((1, 13))
2123
return self._vfs_initialize(a_bzrdir, name=name)
1849
return self._vfs_initialize(a_bzrdir)
2124
1850
if response[0] != 'ok':
2125
1851
raise errors.UnexpectedSmartServerResponse(response)
2126
1852
# Turn the response into a RemoteRepository object.
2155
1881
self._ensure_real()
2156
1882
return self._custom_format.supports_stacking()
2158
def supports_set_append_revisions_only(self):
2160
return self._custom_format.supports_set_append_revisions_only()
2163
class RemoteBranch(branch.Branch, _RpcHelper, lock._RelockDebugMixin):
1885
class RemoteBranch(branch.Branch, _RpcHelper):
2164
1886
"""Branch stored on a server accessed by HPSS RPC.
2166
1888
At the moment most operations are mapped down to simple file operations.
2169
1891
def __init__(self, remote_bzrdir, remote_repository, real_branch=None,
2170
_client=None, format=None, setup_stacking=True, name=None):
1892
_client=None, format=None, setup_stacking=True):
2171
1893
"""Create a RemoteBranch instance.
2173
1895
:param real_branch: An optional local implementation of the branch
2179
1901
:param setup_stacking: If True make an RPC call to determine the
2180
1902
stacked (or not) status of the branch. If False assume the branch
2181
1903
is not stacked.
2182
:param name: Colocated branch name
2184
1905
# We intentionally don't call the parent class's __init__, because it
2185
1906
# will try to assign to self.tags, which is a property in this subclass.
2186
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
2187
1913
self.bzrdir = remote_bzrdir
2188
1914
if _client is not None:
2189
1915
self._client = _client
2379
2089
return self._vfs_get_tags_bytes()
2380
2090
return response[0]
2382
def _vfs_set_tags_bytes(self, bytes):
2384
return self._real_branch._set_tags_bytes(bytes)
2386
def _set_tags_bytes(self, bytes):
2387
medium = self._client._medium
2388
if medium._is_remote_before((1, 18)):
2389
self._vfs_set_tags_bytes(bytes)
2393
self._remote_path(), self._lock_token, self._repo_lock_token)
2394
response = self._call_with_body_bytes(
2395
'Branch.set_tags_bytes', args, bytes)
2396
except errors.UnknownSmartMethod:
2397
medium._remember_remote_is_before((1, 18))
2398
self._vfs_set_tags_bytes(bytes)
2400
2092
def lock_read(self):
2401
"""Lock the branch for read operations.
2403
:return: A bzrlib.lock.LogicalLockResult.
2405
2093
self.repository.lock_read()
2406
2094
if not self._lock_mode:
2407
self._note_lock('r')
2408
2095
self._lock_mode = 'r'
2409
2096
self._lock_count = 1
2410
2097
if self._real_branch is not None:
2411
2098
self._real_branch.lock_read()
2413
2100
self._lock_count += 1
2414
return lock.LogicalLockResult(self.unlock)
2416
2102
def _remote_lock_write(self, token):
2417
2103
if token is None:
2418
2104
branch_token = repo_token = ''
2420
2106
branch_token = token
2421
repo_token = self.repository.lock_write().repository_token
2107
repo_token = self.repository.lock_write()
2422
2108
self.repository.unlock()
2423
2109
err_context = {'token': token}
2425
response = self._call(
2426
'Branch.lock_write', self._remote_path(), branch_token,
2427
repo_token or '', **err_context)
2428
except errors.LockContention, e:
2429
# The LockContention from the server doesn't have any
2430
# information about the lock_url. We re-raise LockContention
2431
# with valid lock_url.
2432
raise errors.LockContention('(remote lock)',
2433
self.repository.base.split('.bzr/')[0])
2110
response = self._call(
2111
'Branch.lock_write', self._remote_path(), branch_token,
2112
repo_token or '', **err_context)
2434
2113
if response[0] != 'ok':
2435
2114
raise errors.UnexpectedSmartServerResponse(response)
2436
2115
ok, branch_token, repo_token = response
2525
2206
raise NotImplementedError(self.dont_leave_lock_in_place)
2526
2207
self._leave_lock = False
2529
def get_rev_id(self, revno, history=None):
2531
return _mod_revision.NULL_REVISION
2532
last_revision_info = self.last_revision_info()
2533
ok, result = self.repository.get_rev_id_for_revno(
2534
revno, last_revision_info)
2537
missing_parent = result[1]
2538
# Either the revision named by the server is missing, or its parent
2539
# is. Call get_parent_map to determine which, so that we report a
2541
parent_map = self.repository.get_parent_map([missing_parent])
2542
if missing_parent in parent_map:
2543
missing_parent = parent_map[missing_parent]
2544
raise errors.RevisionNotPresent(missing_parent, self.repository)
2546
2209
def _last_revision_info(self):
2547
2210
response = self._call('Branch.last_revision_info', self._remote_path())
2548
2211
if response[0] != 'ok':
2797
2457
medium = self._branch._client._medium
2798
2458
if medium._is_remote_before((1, 14)):
2799
2459
return self._vfs_set_option(value, name, section)
2800
if isinstance(value, dict):
2801
if medium._is_remote_before((2, 2)):
2802
return self._vfs_set_option(value, name, section)
2803
return self._set_config_option_dict(value, name, section)
2805
return self._set_config_option(value, name, section)
2807
def _set_config_option(self, value, name, section):
2809
2461
path = self._branch._remote_path()
2810
2462
response = self._branch._client.call('Branch.set_config_option',
2811
2463
path, self._branch._lock_token, self._branch._repo_lock_token,
2812
2464
value.encode('utf8'), name, section or '')
2813
2465
except errors.UnknownSmartMethod:
2814
medium = self._branch._client._medium
2815
2466
medium._remember_remote_is_before((1, 14))
2816
2467
return self._vfs_set_option(value, name, section)
2817
2468
if response != ():
2818
2469
raise errors.UnexpectedSmartServerResponse(response)
2820
def _serialize_option_dict(self, option_dict):
2822
for key, value in option_dict.items():
2823
if isinstance(key, unicode):
2824
key = key.encode('utf8')
2825
if isinstance(value, unicode):
2826
value = value.encode('utf8')
2827
utf8_dict[key] = value
2828
return bencode.bencode(utf8_dict)
2830
def _set_config_option_dict(self, value, name, section):
2832
path = self._branch._remote_path()
2833
serialised_dict = self._serialize_option_dict(value)
2834
response = self._branch._client.call(
2835
'Branch.set_config_option_dict',
2836
path, self._branch._lock_token, self._branch._repo_lock_token,
2837
serialised_dict, name, section or '')
2838
except errors.UnknownSmartMethod:
2839
medium = self._branch._client._medium
2840
medium._remember_remote_is_before((2, 2))
2841
return self._vfs_set_option(value, name, section)
2843
raise errors.UnexpectedSmartServerResponse(response)
2845
2471
def _real_object(self):
2846
2472
self._branch._ensure_real()
2847
2473
return self._branch._real_branch
2930
2556
'Missing key %r in context %r', key_err.args[0], context)
2933
if err.error_verb == 'IncompatibleRepositories':
2934
raise errors.IncompatibleRepositories(err.error_args[0],
2935
err.error_args[1], err.error_args[2])
2936
elif err.error_verb == 'NoSuchRevision':
2559
if err.error_verb == 'NoSuchRevision':
2937
2560
raise NoSuchRevision(find('branch'), err.error_args[0])
2938
2561
elif err.error_verb == 'nosuchrevision':
2939
2562
raise NoSuchRevision(find('repository'), err.error_args[0])
2940
elif err.error_verb == 'nobranch':
2941
if len(err.error_args) >= 1:
2942
extra = err.error_args[0]
2945
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)
2947
2565
elif err.error_verb == 'norepository':
2948
2566
raise errors.NoRepositoryPresent(find('bzrdir'))
2949
2567
elif err.error_verb == 'LockContention':