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
revision as _mod_revision,
36
from bzrlib.branch import BranchReferenceFormat, BranchWriteLockResult
36
from bzrlib.branch import BranchReferenceFormat
37
37
from bzrlib.bzrdir import BzrDir, RemoteBzrDirFormat
38
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
38
from bzrlib.decorators import needs_read_lock, needs_write_lock
39
39
from bzrlib.errors import (
41
41
SmartProtocolError,
62
62
except errors.ErrorFromSmartServer, err:
63
63
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
65
def _call_with_body_bytes_expecting_body(self, method, args, body_bytes,
112
105
self._client = client._SmartClient(medium)
114
107
self._client = _client
121
return '%s(%r)' % (self.__class__.__name__, self._client)
123
def _probe_bzrdir(self):
124
medium = self._client._medium
125
110
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
111
response = self._call('BzrDir.open', path)
152
112
if response not in [('yes',), ('no',)]:
153
113
raise errors.UnexpectedSmartServerResponse(response)
154
114
if response == ('no',):
155
raise errors.NotBranchError(path=self.root_transport.base)
115
raise errors.NotBranchError(path=transport.base)
157
117
def _ensure_real(self):
158
118
"""Ensure that there is a _real_bzrdir set.
245
201
self._ensure_real()
246
202
self._real_bzrdir.destroy_repository()
248
def create_branch(self, name=None):
204
def create_branch(self):
249
205
# as per meta1 formats - just delegate to the format object which may
250
206
# be parameterised.
251
real_branch = self._format.get_branch_format().initialize(self,
207
real_branch = self._format.get_branch_format().initialize(self)
253
208
if not isinstance(real_branch, RemoteBranch):
254
result = RemoteBranch(self, self.find_repository(), real_branch,
209
result = RemoteBranch(self, self.find_repository(), real_branch)
257
211
result = real_branch
258
212
# BzrDir.clone_on_transport() uses the result of create_branch but does
264
218
self._next_open_branch_result = result
267
def destroy_branch(self, name=None):
221
def destroy_branch(self):
268
222
"""See BzrDir.destroy_branch"""
269
223
self._ensure_real()
270
self._real_bzrdir.destroy_branch(name=name)
224
self._real_bzrdir.destroy_branch()
271
225
self._next_open_branch_result = None
273
227
def create_workingtree(self, revision_id=None, from_branch=None):
274
228
raise errors.NotLocalUrl(self.transport.base)
276
def find_branch_format(self, name=None):
230
def find_branch_format(self):
277
231
"""Find the branch 'format' for this bzrdir.
279
233
This might be a synthetic object for e.g. RemoteBranch and SVN.
281
b = self.open_branch(name=name)
235
b = self.open_branch()
284
def get_branch_reference(self, name=None):
238
def get_branch_reference(self):
285
239
"""See BzrDir.get_branch_reference()."""
287
# XXX JRV20100304: Support opening colocated branches
288
raise errors.NoColocatedBranchSupport(self)
289
240
response = self._get_branch_reference()
290
241
if response[0] == 'ref':
291
242
return response[1]
295
246
def _get_branch_reference(self):
296
247
path = self._path_for_remote_call(self._client)
297
248
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):
249
if not medium._is_remote_before((1, 13)):
307
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)
308
255
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'):
256
medium._remember_remote_is_before((1, 13))
257
response = self._call('BzrDir.open_branch', path)
258
if response[0] != 'ok':
322
259
raise errors.UnexpectedSmartServerResponse(response)
260
if response[1] != '':
261
return ('ref', response[1])
263
return ('branch', '')
325
def _get_tree_branch(self, name=None):
265
def _get_tree_branch(self):
326
266
"""See BzrDir._get_tree_branch()."""
327
return None, self.open_branch(name=name)
267
return None, self.open_branch()
329
def open_branch(self, name=None, unsupported=False,
330
ignore_fallbacks=False):
269
def open_branch(self, _unsupported=False, ignore_fallbacks=False):
332
271
raise NotImplementedError('unsupported flag support not implemented yet.')
333
272
if self._next_open_branch_result is not None:
334
273
# See create_branch for details.
339
278
if response[0] == 'ref':
340
279
# a branch reference, use the existing BranchReference logic.
341
280
format = BranchReferenceFormat()
342
return format.open(self, name=name, _found=True,
343
location=response[1], ignore_fallbacks=ignore_fallbacks)
281
return format.open(self, _found=True, location=response[1],
282
ignore_fallbacks=ignore_fallbacks)
344
283
branch_format_name = response[1]
345
284
if not branch_format_name:
346
285
branch_format_name = None
347
286
format = RemoteBranchFormat(network_name=branch_format_name)
348
287
return RemoteBranch(self, self.find_repository(), format=format,
349
setup_stacking=not ignore_fallbacks, name=name)
288
setup_stacking=not ignore_fallbacks)
351
290
def _open_repo_v1(self, path):
352
291
verb = 'BzrDir.find_repository'
624
544
return self._custom_format._fetch_reconcile
626
546
def get_format_description(self):
628
return 'Remote: ' + self._custom_format.get_format_description()
547
return 'bzr remote repository'
630
549
def __eq__(self, other):
631
550
return self.__class__ is other.__class__
552
def check_conversion_target(self, target_format):
553
if self.rich_root_data and not target_format.rich_root_data:
554
raise errors.BadConversionTarget(
555
'Does not support rich root data.', target_format)
556
if (self.supports_tree_reference and
557
not getattr(target_format, 'supports_tree_reference', False)):
558
raise errors.BadConversionTarget(
559
'Does not support nested trees', target_format)
633
561
def network_name(self):
634
562
if self._network_name:
635
563
return self._network_name
756
665
self._ensure_real()
757
666
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)
795
668
def _ensure_real(self):
796
669
"""Ensure that there is a _real_repository set.
876
744
"""Return a source for streaming from this repository."""
877
745
return RemoteStreamSource(self, to_format)
880
747
def has_revision(self, revision_id):
881
"""True if this repository has a copy of the revision."""
882
# Copy of bzrlib.repository.Repository.has_revision
883
return revision_id in self.has_revisions((revision_id,))
748
"""See Repository.has_revision()."""
749
if revision_id == NULL_REVISION:
750
# The null revision is always present.
752
path = self.bzrdir._path_for_remote_call(self._client)
753
response = self._call('Repository.has_revision', path, revision_id)
754
if response[0] not in ('yes', 'no'):
755
raise errors.UnexpectedSmartServerResponse(response)
756
if response[0] == 'yes':
758
for fallback_repo in self._fallback_repositories:
759
if fallback_repo.has_revision(revision_id):
886
763
def has_revisions(self, revision_ids):
887
"""Probe to find out the presence of multiple revisions.
889
:param revision_ids: An iterable of revision_ids.
890
:return: A set of the revision_ids that were present.
892
# Copy of bzrlib.repository.Repository.has_revisions
893
parent_map = self.get_parent_map(revision_ids)
894
result = set(parent_map)
895
if _mod_revision.NULL_REVISION in revision_ids:
896
result.add(_mod_revision.NULL_REVISION)
764
"""See Repository.has_revisions()."""
765
# FIXME: This does many roundtrips, particularly when there are
766
# fallback repositories. -- mbp 20080905
768
for revision_id in revision_ids:
769
if self.has_revision(revision_id):
770
result.add(revision_id)
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
773
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
774
return (self.__class__ is other.__class__ and
917
775
self.bzrdir.transport.base == other.bzrdir.transport.base)
921
779
parents_provider = self._make_parents_provider(other_repository)
922
780
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
782
def gather_stats(self, revid=None, committers=None):
934
783
"""See Repository.gather_stats()."""
935
784
path = self.bzrdir._path_for_remote_call(self._client)
995
844
def is_write_locked(self):
996
845
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
847
def lock_read(self):
1004
"""Lock the repository for read operations.
1006
:return: A bzrlib.lock.LogicalLockResult.
1008
848
# wrong eventually - want a local lock cache context
1009
849
if not self._lock_mode:
1010
self._note_lock('r')
1011
850
self._lock_mode = 'r'
1012
851
self._lock_count = 1
1013
852
self._unstacked_provider.enable_cache(cache_misses=True)
1014
853
if self._real_repository is not None:
1015
854
self._real_repository.lock_read()
1016
for repo in self._fallback_repositories:
1019
856
self._lock_count += 1
1020
return lock.LogicalLockResult(self.unlock)
857
for repo in self._fallback_repositories:
1022
860
def _remote_lock_write(self, token):
1023
861
path = self.bzrdir._path_for_remote_call(self._client)
1244
1075
# We need to accumulate additional repositories here, to pass them in
1245
1076
# 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
1078
self._fallback_repositories.append(repository)
1254
1079
# If self._real_repository was parameterised already (e.g. because a
1255
1080
# _real_branch had its get_stacked_on_url method called), then the
1256
1081
# repository to be added may already be in the _real_repositories list.
1257
1082
if self._real_repository is not None:
1258
fallback_locations = [repo.user_url for repo in
1083
fallback_locations = [repo.bzrdir.root_transport.base for repo in
1259
1084
self._real_repository._fallback_repositories]
1260
if repository.user_url not in fallback_locations:
1085
if repository.bzrdir.root_transport.base not in fallback_locations:
1261
1086
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
1088
def add_inventory(self, revid, inv, parents):
1274
1089
self._ensure_real()
1275
1090
return self._real_repository.add_inventory(revid, inv, parents)
1277
1092
def add_inventory_by_delta(self, basis_revision_id, delta, new_revision_id,
1278
parents, basis_inv=None, propagate_caches=False):
1279
1094
self._ensure_real()
1280
1095
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)
1096
delta, new_revision_id, parents)
1284
1098
def add_revision(self, rev_id, rev, inv=None, config=None):
1285
1099
self._ensure_real()
1315
1129
return self._real_repository.make_working_trees()
1317
1131
def refresh_data(self):
1318
"""Re-read any data needed to synchronise with disk.
1132
"""Re-read any data needed to to synchronise with disk.
1320
1134
This method is intended to be called after another repository instance
1321
1135
(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.
1136
repository. It may not be called during a write group, but may be
1137
called at any other time.
1139
if self.is_in_write_group():
1140
raise errors.InternalBzrError(
1141
"May not refresh_data while in a write group.")
1328
1142
if self._real_repository is not None:
1329
1143
self._real_repository.refresh_data()
1791
1605
def insert_stream(self, stream, src_format, resume_tokens):
1792
1606
target = self.target_repo
1793
target._unstacked_provider.missing_keys.clear()
1794
candidate_calls = [('Repository.insert_stream_1.19', (1, 19))]
1795
1607
if target._lock_token:
1796
candidate_calls.append(('Repository.insert_stream_locked', (1, 14)))
1797
lock_args = (target._lock_token or '',)
1608
verb = 'Repository.insert_stream_locked'
1609
extra_args = (target._lock_token or '',)
1610
required_version = (1, 14)
1799
candidate_calls.append(('Repository.insert_stream', (1, 13)))
1612
verb = 'Repository.insert_stream'
1614
required_version = (1, 13)
1801
1615
client = target._client
1802
1616
medium = client._medium
1617
if medium._is_remote_before(required_version):
1618
# No possible way this can work.
1619
return self._insert_real(stream, src_format, resume_tokens)
1803
1620
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.
1621
if not resume_tokens:
1622
# XXX: Ugly but important for correctness, *will* be fixed during
1623
# 1.13 cycle. Pushing a stream that is interrupted results in a
1624
# fallback to the _real_repositories sink *with a partial stream*.
1625
# Thats bad because we insert less data than bzr expected. To avoid
1626
# this we do a trial push to make sure the verb is accessible, and
1627
# do not fallback when actually pushing the stream. A cleanup patch
1628
# is going to look at rewinding/restarting the stream/partial
1817
1630
byte_stream = smart_repo._stream_to_byte_stream([], src_format)
1819
1632
response = client.call_with_body_stream(
1820
(verb, path, '') + lock_args, byte_stream)
1633
(verb, path, '') + extra_args, byte_stream)
1821
1634
except errors.UnknownSmartMethod:
1822
1635
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)
1636
return self._insert_real(stream, src_format, resume_tokens)
1837
1637
byte_stream = smart_repo._stream_to_byte_stream(
1838
1638
stream, src_format)
1839
1639
resume_tokens = ' '.join(resume_tokens)
1840
1640
response = client.call_with_body_stream(
1841
(verb, path, resume_tokens) + lock_args, byte_stream)
1641
(verb, path, resume_tokens) + extra_args, byte_stream)
1842
1642
if response[0][0] not in ('ok', 'missing-basis'):
1843
1643
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
1644
if response[0][0] == 'missing-basis':
1851
1645
tokens, missing_keys = bencode.bdecode_as_tuple(response[0][1])
1852
1646
resume_tokens = tokens
1855
1649
self.target_repo.refresh_data()
1856
1650
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
1653
class RemoteStreamSource(repository.StreamSource):
1900
1654
"""Stream data from a remote server."""
1903
1657
if (self.from_repository._fallback_repositories and
1904
1658
self.to_format._fetch_order == 'topological'):
1905
1659
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)
1660
return self.missing_parents_chain(search, [self.from_repository] +
1661
self.from_repository._fallback_repositories)
1924
1663
def _real_stream(self, repo, search):
1925
1664
"""Get a stream for search from repo.
1956
1694
return self._real_stream(repo, search)
1957
1695
client = repo._client
1958
1696
medium = client._medium
1697
if medium._is_remote_before((1, 13)):
1698
# streaming was added in 1.13
1699
return self._real_stream(repo, search)
1959
1700
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
1702
search_bytes = repo._serialise_search_result(search)
1703
response = repo._call_with_body_bytes_expecting_body(
1704
'Repository.get_stream',
1705
(path, self.to_format.network_name()), search_bytes)
1706
response_tuple, response_handler = response
1707
except errors.UnknownSmartMethod:
1708
medium._remember_remote_is_before((1,13))
1979
1709
return self._real_stream(repo, search)
1980
1710
if response_tuple[0] != 'ok':
1981
1711
raise errors.UnexpectedSmartServerResponse(response_tuple)
2062
1791
self._network_name)
2064
1793
def get_format_description(self):
2066
return 'Remote: ' + self._custom_format.get_format_description()
1794
return 'Remote BZR Branch'
2068
1796
def network_name(self):
2069
1797
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)
1799
def open(self, a_bzrdir, ignore_fallbacks=False):
1800
return a_bzrdir.open_branch(ignore_fallbacks=ignore_fallbacks)
2075
def _vfs_initialize(self, a_bzrdir, name):
1802
def _vfs_initialize(self, a_bzrdir):
2076
1803
# Initialisation when using a local bzrdir object, or a non-vfs init
2077
1804
# method is not available on the server.
2078
1805
# self._custom_format is always set - the start of initialize ensures
2080
1807
if isinstance(a_bzrdir, RemoteBzrDir):
2081
1808
a_bzrdir._ensure_real()
2082
result = self._custom_format.initialize(a_bzrdir._real_bzrdir,
1809
result = self._custom_format.initialize(a_bzrdir._real_bzrdir)
2085
1811
# We assume the bzrdir is parameterised; it may not be.
2086
result = self._custom_format.initialize(a_bzrdir, name)
1812
result = self._custom_format.initialize(a_bzrdir)
2087
1813
if (isinstance(a_bzrdir, RemoteBzrDir) and
2088
1814
not isinstance(result, RemoteBranch)):
2089
result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result,
1815
result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result)
2093
def initialize(self, a_bzrdir, name=None):
1818
def initialize(self, a_bzrdir):
2094
1819
# 1) get the network name to use.
2095
1820
if self._custom_format:
2096
1821
network_name = self._custom_format.network_name()
2102
1827
network_name = reference_format.network_name()
2103
1828
# Being asked to create on a non RemoteBzrDir:
2104
1829
if not isinstance(a_bzrdir, RemoteBzrDir):
2105
return self._vfs_initialize(a_bzrdir, name=name)
1830
return self._vfs_initialize(a_bzrdir)
2106
1831
medium = a_bzrdir._client._medium
2107
1832
if medium._is_remote_before((1, 13)):
2108
return self._vfs_initialize(a_bzrdir, name=name)
1833
return self._vfs_initialize(a_bzrdir)
2109
1834
# Creating on a remote bzr dir.
2110
1835
# 2) try direct creation via RPC
2111
1836
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
1837
verb = 'BzrDir.create_branch'
2117
1839
response = a_bzrdir._call(verb, path, network_name)
2118
1840
except errors.UnknownSmartMethod:
2119
1841
# Fallback - use vfs methods
2120
1842
medium._remember_remote_is_before((1, 13))
2121
return self._vfs_initialize(a_bzrdir, name=name)
1843
return self._vfs_initialize(a_bzrdir)
2122
1844
if response[0] != 'ok':
2123
1845
raise errors.UnexpectedSmartServerResponse(response)
2124
1846
# Turn the response into a RemoteRepository object.
2153
1875
self._ensure_real()
2154
1876
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):
1879
class RemoteBranch(branch.Branch, _RpcHelper):
2162
1880
"""Branch stored on a server accessed by HPSS RPC.
2164
1882
At the moment most operations are mapped down to simple file operations.
2167
1885
def __init__(self, remote_bzrdir, remote_repository, real_branch=None,
2168
_client=None, format=None, setup_stacking=True, name=None):
1886
_client=None, format=None, setup_stacking=True):
2169
1887
"""Create a RemoteBranch instance.
2171
1889
:param real_branch: An optional local implementation of the branch
2177
1895
:param setup_stacking: If True make an RPC call to determine the
2178
1896
stacked (or not) status of the branch. If False assume the branch
2179
1897
is not stacked.
2180
:param name: Colocated branch name
2182
1899
# We intentionally don't call the parent class's __init__, because it
2183
1900
# will try to assign to self.tags, which is a property in this subclass.
2184
1901
# And the parent's __init__ doesn't do much anyway.
1902
self._revision_id_to_revno_cache = None
1903
self._partial_revision_id_to_revno_cache = {}
1904
self._revision_history_cache = None
1905
self._last_revision_info_cache = None
1906
self._merge_sorted_revisions_cache = None
2185
1907
self.bzrdir = remote_bzrdir
2186
1908
if _client is not None:
2187
1909
self._client = _client
2377
2083
return self._vfs_get_tags_bytes()
2378
2084
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
2086
def lock_read(self):
2399
"""Lock the branch for read operations.
2401
:return: A bzrlib.lock.LogicalLockResult.
2403
2087
self.repository.lock_read()
2404
2088
if not self._lock_mode:
2405
self._note_lock('r')
2406
2089
self._lock_mode = 'r'
2407
2090
self._lock_count = 1
2408
2091
if self._real_branch is not None:
2409
2092
self._real_branch.lock_read()
2411
2094
self._lock_count += 1
2412
return lock.LogicalLockResult(self.unlock)
2414
2096
def _remote_lock_write(self, token):
2415
2097
if token is None:
2416
2098
branch_token = repo_token = ''
2418
2100
branch_token = token
2419
repo_token = self.repository.lock_write().repository_token
2101
repo_token = self.repository.lock_write()
2420
2102
self.repository.unlock()
2421
2103
err_context = {'token': token}
2422
2104
response = self._call(
2516
2200
raise NotImplementedError(self.dont_leave_lock_in_place)
2517
2201
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
2203
def _last_revision_info(self):
2538
2204
response = self._call('Branch.last_revision_info', self._remote_path())
2539
2205
if response[0] != 'ok':
2887
2550
'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':
2553
if err.error_verb == 'NoSuchRevision':
2894
2554
raise NoSuchRevision(find('branch'), err.error_args[0])
2895
2555
elif err.error_verb == 'nosuchrevision':
2896
2556
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,
2557
elif err.error_tuple == ('nobranch',):
2558
raise errors.NotBranchError(path=find('bzrdir').root_transport.base)
2904
2559
elif err.error_verb == 'norepository':
2905
2560
raise errors.NoRepositoryPresent(find('bzrdir'))
2906
2561
elif err.error_verb == 'LockContention':