13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
17
# TODO: At some point, handle upgrades by just passing the whole request
18
18
# across to run on the server.
153
152
response = self._call(verb, path, stacking)
154
153
except errors.UnknownSmartMethod:
155
medium._remember_remote_is_before((1, 13))
156
154
return self._vfs_cloning_metadir(require_stacking=require_stacking)
157
except errors.UnknownErrorFromSmartServer, err:
158
if err.error_tuple != ('BranchReference',):
160
# We need to resolve the branch reference to determine the
161
# cloning_metadir. This causes unnecessary RPCs to open the
162
# referenced branch (and bzrdir, etc) but only when the caller
163
# didn't already resolve the branch reference.
164
referenced_branch = self.open_branch()
165
return referenced_branch.bzrdir.cloning_metadir()
166
155
if len(response) != 3:
167
156
raise errors.UnexpectedSmartServerResponse(response)
168
157
control_name, repo_name, branch_info = response
238
227
def get_branch_reference(self):
239
228
"""See BzrDir.get_branch_reference()."""
240
response = self._get_branch_reference()
241
if response[0] == 'ref':
246
def _get_branch_reference(self):
247
229
path = self._path_for_remote_call(self._client)
248
medium = self._client._medium
249
if not medium._is_remote_before((1, 13)):
251
response = self._call('BzrDir.open_branchV2', path)
252
if response[0] not in ('ref', 'branch'):
253
raise errors.UnexpectedSmartServerResponse(response)
255
except errors.UnknownSmartMethod:
256
medium._remember_remote_is_before((1, 13))
257
230
response = self._call('BzrDir.open_branch', path)
258
if response[0] != 'ok':
231
if response[0] == 'ok':
232
if response[1] == '':
233
# branch at this location.
236
# a branch reference, use the existing BranchReference logic.
259
239
raise errors.UnexpectedSmartServerResponse(response)
260
if response[1] != '':
261
return ('ref', response[1])
263
return ('branch', '')
265
241
def _get_tree_branch(self):
266
242
"""See BzrDir._get_tree_branch()."""
267
243
return None, self.open_branch()
269
def open_branch(self, _unsupported=False, ignore_fallbacks=False):
245
def open_branch(self, _unsupported=False):
271
247
raise NotImplementedError('unsupported flag support not implemented yet.')
272
248
if self._next_open_branch_result is not None:
274
250
result = self._next_open_branch_result
275
251
self._next_open_branch_result = None
277
response = self._get_branch_reference()
278
if response[0] == 'ref':
253
reference_url = self.get_branch_reference()
254
if reference_url is None:
255
# branch at this location.
256
return RemoteBranch(self, self.find_repository())
279
258
# a branch reference, use the existing BranchReference logic.
280
259
format = BranchReferenceFormat()
281
return format.open(self, _found=True, location=response[1],
282
ignore_fallbacks=ignore_fallbacks)
283
branch_format_name = response[1]
284
if not branch_format_name:
285
branch_format_name = None
286
format = RemoteBranchFormat(network_name=branch_format_name)
287
return RemoteBranch(self, self.find_repository(), format=format,
288
setup_stacking=not ignore_fallbacks)
260
return format.open(self, _found=True, location=reference_url)
290
262
def _open_repo_v1(self, path):
291
263
verb = 'BzrDir.find_repository'
424
392
self._custom_format = None
425
393
self._network_name = None
426
394
self._creating_bzrdir = None
427
self._supports_external_lookups = None
428
self._supports_tree_reference = None
429
self._rich_root_data = None
432
def fast_deltas(self):
434
return self._custom_format.fast_deltas
437
def rich_root_data(self):
438
if self._rich_root_data is None:
440
self._rich_root_data = self._custom_format.rich_root_data
441
return self._rich_root_data
444
def supports_external_lookups(self):
445
if self._supports_external_lookups is None:
447
self._supports_external_lookups = \
448
self._custom_format.supports_external_lookups
449
return self._supports_external_lookups
452
def supports_tree_reference(self):
453
if self._supports_tree_reference is None:
455
self._supports_tree_reference = \
456
self._custom_format.supports_tree_reference
457
return self._supports_tree_reference
459
396
def _vfs_initialize(self, a_bzrdir, shared):
460
397
"""Helper for common code in initialize."""
637
573
return self._real_repository.abort_write_group(
638
574
suppress_errors=suppress_errors)
642
"""Decorate the real repository for now.
644
In the long term a full blown network facility is needed to avoid
645
creating a real repository object locally.
648
return self._real_repository.chk_bytes
650
576
def commit_write_group(self):
651
577
"""Complete a write group on the decorated repository.
653
Smart methods perform operations in a single step so this API
579
Smart methods peform operations in a single step so this api
654
580
is not really applicable except as a compatibility thunk
655
581
for older plugins that don't use e.g. the CommitBuilder
670
596
"""Ensure that there is a _real_repository set.
672
598
Used before calls to self._real_repository.
674
Note that _ensure_real causes many roundtrips to the server which are
675
not desirable, and prevents the use of smart one-roundtrip RPC's to
676
perform complex operations (such as accessing parent data, streaming
677
revisions etc). Adding calls to _ensure_real should only be done when
678
bringing up new functionality, adding fallbacks for smart methods that
679
require a fallback path, and never to replace an existing smart method
680
invocation. If in doubt chat to the bzr network team.
682
600
if self._real_repository is None:
683
601
self.bzrdir._ensure_real()
927
845
if isinstance(repository, RemoteRepository):
928
846
raise AssertionError()
929
847
self._real_repository = repository
930
# three code paths happen here:
931
# 1) old servers, RemoteBranch.open() calls _ensure_real before setting
932
# up stacking. In this case self._fallback_repositories is [], and the
933
# real repo is already setup. Preserve the real repo and
934
# RemoteRepository.add_fallback_repository will avoid adding
936
# 2) new servers, RemoteBranch.open() sets up stacking, and when
937
# ensure_real is triggered from a branch, the real repository to
938
# set already has a matching list with separate instances, but
939
# as they are also RemoteRepositories we don't worry about making the
940
# lists be identical.
941
# 3) new servers, RemoteRepository.ensure_real is triggered before
942
# RemoteBranch.ensure real, in this case we get a repo with no fallbacks
943
# and need to populate it.
944
if (self._fallback_repositories and
945
len(self._real_repository._fallback_repositories) !=
946
len(self._fallback_repositories)):
947
if len(self._real_repository._fallback_repositories):
948
raise AssertionError(
949
"cannot cleanly remove existing _fallback_repositories")
950
848
for fb in self._fallback_repositories:
951
849
self._real_repository.add_fallback_repository(fb)
952
850
if self._lock_mode == 'w':
1071
969
:param repository: A repository.
1073
if not self._format.supports_external_lookups:
1074
raise errors.UnstackableRepositoryFormat(
1075
self._format.network_name(), self.base)
971
# XXX: At the moment the RemoteRepository will allow fallbacks
972
# unconditionally - however, a _real_repository will usually exist,
973
# and may raise an error if it's not accommodated by the underlying
974
# format. Eventually we should check when opening the repository
975
# whether it's willing to allow them or not.
1076
977
# We need to accumulate additional repositories here, to pass them in
1077
978
# on various RPC's.
1081
982
# _real_branch had its get_stacked_on_url method called), then the
1082
983
# repository to be added may already be in the _real_repositories list.
1083
984
if self._real_repository is not None:
1084
fallback_locations = [repo.bzrdir.root_transport.base for repo in
1085
self._real_repository._fallback_repositories]
1086
if repository.bzrdir.root_transport.base not in fallback_locations:
985
if repository not in self._real_repository._fallback_repositories:
1087
986
self._real_repository.add_fallback_repository(repository)
988
# They are also seen by the fallback repository. If it doesn't
989
# exist yet they'll be added then. This implicitly copies them.
1089
992
def add_inventory(self, revid, inv, parents):
1090
993
self._ensure_real()
1129
1032
self._ensure_real()
1130
1033
return self._real_repository.make_working_trees()
1132
def refresh_data(self):
1133
"""Re-read any data needed to to synchronise with disk.
1135
This method is intended to be called after another repository instance
1136
(such as one used by a smart server) has inserted data into the
1137
repository. It may not be called during a write group, but may be
1138
called at any other time.
1140
if self.is_in_write_group():
1141
raise errors.InternalBzrError(
1142
"May not refresh_data while in a write group.")
1143
if self._real_repository is not None:
1144
self._real_repository.refresh_data()
1146
1035
def revision_ids_to_search_result(self, result_set):
1147
1036
"""Convert a set of revision ids to a graph SearchResult."""
1148
1037
result_parents = set()
1167
1056
return repository.InterRepository.get(
1168
1057
other, self).search_missing_revision_ids(revision_id, find_ghosts)
1170
def fetch(self, source, revision_id=None, pb=None, find_ghosts=False,
1172
# No base implementation to use as RemoteRepository is not a subclass
1173
# of Repository; so this is a copy of Repository.fetch().
1174
if fetch_spec is not None and revision_id is not None:
1175
raise AssertionError(
1176
"fetch_spec and revision_id are mutually exclusive.")
1177
if self.is_in_write_group():
1178
raise errors.InternalBzrError(
1179
"May not fetch while in a write group.")
1180
# fast path same-url fetch operations
1181
if self.has_same_location(source) and fetch_spec is None:
1059
def fetch(self, source, revision_id=None, pb=None, find_ghosts=False):
1060
# Not delegated to _real_repository so that InterRepository.get has a
1061
# chance to find an InterRepository specialised for RemoteRepository.
1062
if self.has_same_location(source):
1182
1063
# check that last_revision is in 'from' and then return a
1183
1064
# no-operation.
1184
1065
if (revision_id is not None and
1185
1066
not revision.is_null(revision_id)):
1186
1067
self.get_revision(revision_id)
1188
# if there is no specific appropriate InterRepository, this will get
1189
# the InterRepository base class, which raises an
1190
# IncompatibleRepositories when asked to fetch.
1191
1069
inter = repository.InterRepository.get(source, self)
1192
return inter.fetch(revision_id=revision_id, pb=pb,
1193
find_ghosts=find_ghosts, fetch_spec=fetch_spec)
1071
return inter.fetch(revision_id=revision_id, pb=pb, find_ghosts=find_ghosts)
1072
except NotImplementedError:
1073
raise errors.IncompatibleRepositories(source, self)
1195
1075
def create_bundle(self, target, base, fileobj, format=None):
1196
1076
self._ensure_real()
1227
1107
# We already found out that the server can't understand
1228
1108
# Repository.get_parent_map requests, so just fetch the whole
1231
# Note that this reads the whole graph, when only some keys are
1232
# wanted. On this old server there's no way (?) to get them all
1233
# in one go, and the user probably will have seen a warning about
1234
# the server being old anyhow.
1235
rg = self._get_revision_graph(None)
1236
# There is an API discrepancy between get_parent_map and
1110
# XXX: Note that this will issue a deprecation warning. This is ok
1111
# :- its because we're working with a deprecated server anyway, and
1112
# the user will almost certainly have seen a warning about the
1113
# server version already.
1114
rg = self.get_revision_graph()
1115
# There is an api discrepency between get_parent_map and
1237
1116
# get_revision_graph. Specifically, a "key:()" pair in
1238
1117
# get_revision_graph just means a node has no parents. For
1239
1118
# "get_parent_map" it means the node is a ghost. So fix up the
1269
1148
# TODO: Manage this incrementally to avoid covering the same path
1270
1149
# repeatedly. (The server will have to on each request, but the less
1271
1150
# work done the better).
1273
# Negative caching notes:
1274
# new server sends missing when a request including the revid
1275
# 'include-missing:' is present in the request.
1276
# missing keys are serialised as missing:X, and we then call
1277
# provider.note_missing(X) for-all X
1278
1151
parents_map = self._unstacked_provider.get_cached_map()
1279
1152
if parents_map is None:
1280
1153
# Repository is not locked, so there's no cache.
1281
1154
parents_map = {}
1282
# start_set is all the keys in the cache
1283
1155
start_set = set(parents_map)
1284
# result set is all the references to keys in the cache
1285
1156
result_parents = set()
1286
1157
for parents in parents_map.itervalues():
1287
1158
result_parents.update(parents)
1288
1159
stop_keys = result_parents.difference(start_set)
1289
# We don't need to send ghosts back to the server as a position to
1291
stop_keys.difference_update(self._unstacked_provider.missing_keys)
1292
key_count = len(parents_map)
1293
if (NULL_REVISION in result_parents
1294
and NULL_REVISION in self._unstacked_provider.missing_keys):
1295
# If we pruned NULL_REVISION from the stop_keys because it's also
1296
# in our cache of "missing" keys we need to increment our key count
1297
# by 1, because the reconsitituted SearchResult on the server will
1298
# still consider NULL_REVISION to be an included key.
1300
1160
included_keys = start_set.intersection(result_parents)
1301
1161
start_set.difference_update(included_keys)
1302
recipe = ('manual', start_set, stop_keys, key_count)
1162
recipe = (start_set, stop_keys, len(parents_map))
1303
1163
body = self._serialise_search_recipe(recipe)
1304
1164
path = self.bzrdir._path_for_remote_call(self._client)
1305
1165
for key in keys:
1374
1234
return self._real_repository.all_revision_ids()
1376
1236
@needs_read_lock
1377
def get_deltas_for_revisions(self, revisions, specific_fileids=None):
1237
def get_deltas_for_revisions(self, revisions):
1378
1238
self._ensure_real()
1379
return self._real_repository.get_deltas_for_revisions(revisions,
1380
specific_fileids=specific_fileids)
1239
return self._real_repository.get_deltas_for_revisions(revisions)
1382
1241
@needs_read_lock
1383
def get_revision_delta(self, revision_id, specific_fileids=None):
1242
def get_revision_delta(self, revision_id):
1384
1243
self._ensure_real()
1385
return self._real_repository.get_revision_delta(revision_id,
1386
specific_fileids=specific_fileids)
1244
return self._real_repository.get_revision_delta(revision_id)
1388
1246
@needs_read_lock
1389
1247
def revision_trees(self, revision_ids):
1566
1424
:param recipe: A search recipe (start, stop, count).
1567
1425
:return: Serialised bytes.
1569
start_keys = ' '.join(recipe[1])
1570
stop_keys = ' '.join(recipe[2])
1571
count = str(recipe[3])
1427
start_keys = ' '.join(recipe[0])
1428
stop_keys = ' '.join(recipe[1])
1429
count = str(recipe[2])
1572
1430
return '\n'.join((start_keys, stop_keys, count))
1574
def _serialise_search_result(self, search_result):
1575
if isinstance(search_result, graph.PendingAncestryResult):
1576
parts = ['ancestry-of']
1577
parts.extend(search_result.heads)
1579
recipe = search_result.get_recipe()
1580
parts = [recipe[0], self._serialise_search_recipe(recipe)]
1581
return '\n'.join(parts)
1583
1432
def autopack(self):
1584
1433
path = self.bzrdir._path_for_remote_call(self._client)
1606
1460
def insert_stream(self, stream, src_format, resume_tokens):
1607
target = self.target_repo
1608
if target._lock_token:
1609
verb = 'Repository.insert_stream_locked'
1610
extra_args = (target._lock_token or '',)
1611
required_version = (1, 14)
1613
verb = 'Repository.insert_stream'
1615
required_version = (1, 13)
1616
client = target._client
1461
repo = self.target_repo
1462
client = repo._client
1617
1463
medium = client._medium
1618
if medium._is_remote_before(required_version):
1464
if medium._is_remote_before((1, 13)):
1619
1465
# No possible way this can work.
1620
1466
return self._insert_real(stream, src_format, resume_tokens)
1621
path = target.bzrdir._path_for_remote_call(client)
1467
path = repo.bzrdir._path_for_remote_call(client)
1622
1468
if not resume_tokens:
1623
1469
# XXX: Ugly but important for correctness, *will* be fixed during
1624
1470
# 1.13 cycle. Pushing a stream that is interrupted results in a
1631
1477
byte_stream = smart_repo._stream_to_byte_stream([], src_format)
1633
1479
response = client.call_with_body_stream(
1634
(verb, path, '') + extra_args, byte_stream)
1480
('Repository.insert_stream', path, ''), byte_stream)
1635
1481
except errors.UnknownSmartMethod:
1636
medium._remember_remote_is_before(required_version)
1482
medium._remember_remote_is_before((1,13))
1637
1483
return self._insert_real(stream, src_format, resume_tokens)
1638
1484
byte_stream = smart_repo._stream_to_byte_stream(
1639
1485
stream, src_format)
1640
1486
resume_tokens = ' '.join(resume_tokens)
1641
1487
response = client.call_with_body_stream(
1642
(verb, path, resume_tokens) + extra_args, byte_stream)
1488
('Repository.insert_stream', path, resume_tokens), byte_stream)
1643
1489
if response[0][0] not in ('ok', 'missing-basis'):
1644
1490
raise errors.UnexpectedSmartServerResponse(response)
1645
1491
if response[0][0] == 'missing-basis':
1655
1505
"""Stream data from a remote server."""
1657
1507
def get_stream(self, search):
1658
if (self.from_repository._fallback_repositories and
1659
self.to_format._fetch_order == 'topological'):
1660
return self._real_stream(self.from_repository, search)
1661
return self.missing_parents_chain(search, [self.from_repository] +
1662
self.from_repository._fallback_repositories)
1664
def _real_stream(self, repo, search):
1665
"""Get a stream for search from repo.
1667
This never called RemoteStreamSource.get_stream, and is a heler
1668
for RemoteStreamSource._get_stream to allow getting a stream
1669
reliably whether fallback back because of old servers or trying
1670
to stream from a non-RemoteRepository (which the stacked support
1673
source = repo._get_source(self.to_format)
1674
if isinstance(source, RemoteStreamSource):
1675
return repository.StreamSource.get_stream(source, search)
1676
return source.get_stream(search)
1678
def _get_stream(self, repo, search):
1679
"""Core worker to get a stream from repo for search.
1681
This is used by both get_stream and the stacking support logic. It
1682
deliberately gets a stream for repo which does not need to be
1683
self.from_repository. In the event that repo is not Remote, or
1684
cannot do a smart stream, a fallback is made to the generic
1685
repository._get_stream() interface, via self._real_stream.
1687
In the event of stacking, streams from _get_stream will not
1688
contain all the data for search - this is normal (see get_stream).
1690
:param repo: A repository.
1691
:param search: A search.
1693
# Fallbacks may be non-smart
1694
if not isinstance(repo, RemoteRepository):
1695
return self._real_stream(repo, search)
1508
# streaming with fallback repositories is not well defined yet: The
1509
# remote repository cannot see the fallback repositories, and thus
1510
# cannot satisfy the entire search in the general case. Likewise the
1511
# fallback repositories cannot reify the search to determine what they
1512
# should send. It likely needs a return value in the stream listing the
1513
# edge of the search to resume from in fallback repositories.
1514
if self.from_repository._fallback_repositories:
1515
return repository.StreamSource.get_stream(self, search)
1516
repo = self.from_repository
1696
1517
client = repo._client
1697
1518
medium = client._medium
1698
1519
if medium._is_remote_before((1, 13)):
1699
# streaming was added in 1.13
1700
return self._real_stream(repo, search)
1520
# No possible way this can work.
1521
return repository.StreamSource.get_stream(self, search)
1701
1522
path = repo.bzrdir._path_for_remote_call(client)
1703
search_bytes = repo._serialise_search_result(search)
1524
recipe = repo._serialise_search_recipe(search._recipe)
1704
1525
response = repo._call_with_body_bytes_expecting_body(
1705
1526
'Repository.get_stream',
1706
(path, self.to_format.network_name()), search_bytes)
1527
(path, self.to_format.network_name()), recipe)
1707
1528
response_tuple, response_handler = response
1708
1529
except errors.UnknownSmartMethod:
1709
1530
medium._remember_remote_is_before((1,13))
1710
return self._real_stream(repo, search)
1531
return repository.StreamSource.get_stream(self, search)
1711
1532
if response_tuple[0] != 'ok':
1712
1533
raise errors.UnexpectedSmartServerResponse(response_tuple)
1713
1534
byte_stream = response_handler.read_streamed_body()
1718
1539
src_format.network_name(), repo._format.network_name()))
1721
def missing_parents_chain(self, search, sources):
1722
"""Chain multiple streams together to handle stacking.
1724
:param search: The overall search to satisfy with streams.
1725
:param sources: A list of Repository objects to query.
1727
self.serialiser = self.to_format._serializer
1728
self.seen_revs = set()
1729
self.referenced_revs = set()
1730
# If there are heads in the search, or the key count is > 0, we are not
1732
while not search.is_empty() and len(sources) > 1:
1733
source = sources.pop(0)
1734
stream = self._get_stream(source, search)
1735
for kind, substream in stream:
1736
if kind != 'revisions':
1737
yield kind, substream
1739
yield kind, self.missing_parents_rev_handler(substream)
1740
search = search.refine(self.seen_revs, self.referenced_revs)
1741
self.seen_revs = set()
1742
self.referenced_revs = set()
1743
if not search.is_empty():
1744
for kind, stream in self._get_stream(sources[0], search):
1747
def missing_parents_rev_handler(self, substream):
1748
for content in substream:
1749
revision_bytes = content.get_bytes_as('fulltext')
1750
revision = self.serialiser.read_revision_from_string(revision_bytes)
1751
self.seen_revs.add(content.key[-1])
1752
self.referenced_revs.update(revision.parent_ids)
1756
1543
class RemoteBranchLockableFiles(LockableFiles):
1757
1544
"""A 'LockableFiles' implementation that talks to a smart server.
1776
1563
class RemoteBranchFormat(branch.BranchFormat):
1778
def __init__(self, network_name=None):
1779
1566
super(RemoteBranchFormat, self).__init__()
1780
1567
self._matchingbzrdir = RemoteBzrDirFormat()
1781
1568
self._matchingbzrdir.set_branch_format(self)
1782
1569
self._custom_format = None
1783
self._network_name = network_name
1785
1571
def __eq__(self, other):
1786
1572
return (isinstance(other, RemoteBranchFormat) and
1787
1573
self.__dict__ == other.__dict__)
1789
def _ensure_real(self):
1790
if self._custom_format is None:
1791
self._custom_format = branch.network_format_registry.get(
1794
1575
def get_format_description(self):
1795
1576
return 'Remote BZR Branch'
1797
1578
def network_name(self):
1798
1579
return self._network_name
1800
def open(self, a_bzrdir, ignore_fallbacks=False):
1801
return a_bzrdir.open_branch(ignore_fallbacks=ignore_fallbacks)
1581
def open(self, a_bzrdir):
1582
return a_bzrdir.open_branch()
1803
1584
def _vfs_initialize(self, a_bzrdir):
1804
1585
# Initialisation when using a local bzrdir object, or a non-vfs init
1840
1621
response = a_bzrdir._call(verb, path, network_name)
1841
1622
except errors.UnknownSmartMethod:
1842
1623
# Fallback - use vfs methods
1843
medium._remember_remote_is_before((1, 13))
1844
1624
return self._vfs_initialize(a_bzrdir)
1845
1625
if response[0] != 'ok':
1846
1626
raise errors.UnexpectedSmartServerResponse(response)
1847
1627
# Turn the response into a RemoteRepository object.
1848
format = RemoteBranchFormat(network_name=response[1])
1628
format = RemoteBranchFormat()
1629
format._network_name = response[1]
1849
1630
repo_format = response_tuple_to_repo_format(response[3:])
1850
1631
if response[2] == '':
1851
1632
repo_bzrdir = a_bzrdir
1939
1711
if real_branch is not None:
1940
1712
self._format._network_name = \
1941
1713
self._real_branch._format.network_name()
1715
# # XXX: Need to get this from BzrDir.open_branch's return value.
1716
# self._ensure_real()
1717
# self._format._network_name = \
1718
# self._real_branch._format.network_name()
1943
1720
self._format = format
1944
if not self._format._network_name:
1945
# Did not get from open_branchV2 - old server.
1947
self._format._network_name = \
1948
self._real_branch._format.network_name()
1949
self.tags = self._format.make_tags(self)
1950
1721
# The base class init is not called, so we duplicate this:
1951
1722
hooks = branch.Branch.hooks['open']
1952
1723
for hook in hooks:
1962
1733
except (errors.NotStacked, errors.UnstackableBranchFormat,
1963
1734
errors.UnstackableRepositoryFormat), e:
1965
self._activate_fallback_location(fallback_url)
1967
def _get_config(self):
1968
return RemoteBranchConfig(self)
1736
# it's relative to this branch...
1737
fallback_url = urlutils.join(self.base, fallback_url)
1738
transports = [self.bzrdir.root_transport]
1739
if self._real_branch is not None:
1740
# The real repository is setup already:
1741
transports.append(self._real_branch._transport)
1742
self.repository.add_fallback_repository(
1743
self.repository._real_repository._fallback_repositories[0])
1745
stacked_on = branch.Branch.open(fallback_url,
1746
possible_transports=transports)
1747
self.repository.add_fallback_repository(stacked_on.repository)
1970
1749
def _get_real_transport(self):
1971
1750
# if we try vfs access, return the real branch's vfs transport
2069
1848
raise errors.UnexpectedSmartServerResponse(response)
2070
1849
return response[1]
2072
def _vfs_get_tags_bytes(self):
2074
return self._real_branch._get_tags_bytes()
2076
def _get_tags_bytes(self):
2077
medium = self._client._medium
2078
if medium._is_remote_before((1, 13)):
2079
return self._vfs_get_tags_bytes()
2081
response = self._call('Branch.get_tags_bytes', self._remote_path())
2082
except errors.UnknownSmartMethod:
2083
medium._remember_remote_is_before((1, 13))
2084
return self._vfs_get_tags_bytes()
2087
1851
def lock_read(self):
2088
1852
self.repository.lock_read()
2089
1853
if not self._lock_mode:
2380
2150
self.set_revision_history(self._lefthand_history(revision_id,
2381
2151
last_rev=last_rev,other_branch=other_branch))
2156
return self._real_branch.tags
2383
2158
def set_push_location(self, location):
2384
2159
self._ensure_real()
2385
2160
return self._real_branch.set_push_location(location)
2388
class RemoteBranchConfig(object):
2389
"""A Config that reads from a smart branch and writes via smart methods.
2391
It is a low-level object that considers config data to be name/value pairs
2392
that may be associated with a section. Assigning meaning to the these
2393
values is done at higher levels like bzrlib.config.TreeConfig.
2396
def __init__(self, branch):
2397
self._branch = branch
2399
def get_option(self, name, section=None, default=None):
2400
"""Return the value associated with a named option.
2402
:param name: The name of the value
2403
:param section: The section the option is in (if any)
2404
:param default: The value to return if the value is not set
2405
:return: The value or default value
2407
configobj = self._get_configobj()
2409
section_obj = configobj
2412
section_obj = configobj[section]
2415
return section_obj.get(name, default)
2417
def _get_configobj(self):
2418
path = self._branch._remote_path()
2419
response = self._branch._client.call_expecting_body(
2420
'Branch.get_config_file', path)
2421
if response[0][0] != 'ok':
2422
raise UnexpectedSmartServerResponse(response)
2423
lines = response[1].read_body_bytes().splitlines()
2424
return config.ConfigObj(lines, encoding='utf-8')
2426
def set_option(self, value, name, section=None):
2427
"""Set the value associated with a named option.
2429
:param value: The value to set
2430
:param name: The name of the value to set
2431
:param section: The section the option is in (if any)
2433
medium = self._branch._client._medium
2434
if medium._is_remote_before((1, 14)):
2435
return self._vfs_set_option(value, name, section)
2437
path = self._branch._remote_path()
2438
response = self._branch._client.call('Branch.set_config_option',
2439
path, self._branch._lock_token, self._branch._repo_lock_token,
2440
value.encode('utf8'), name, section or '')
2441
except errors.UnknownSmartMethod:
2442
medium._remember_remote_is_before((1, 14))
2443
return self._vfs_set_option(value, name, section)
2445
raise errors.UnexpectedSmartServerResponse(response)
2447
def _vfs_set_option(self, value, name, section=None):
2448
self._branch._ensure_real()
2449
return self._branch._real_branch._get_config().set_option(
2450
value, name, section)
2453
2163
def _extract_tar(tar, to_dir):
2454
2164
"""Extract all the contents of a tarfile object.