131
154
# be parameterised.
132
155
real_branch = self._format.get_branch_format().initialize(self)
133
156
if not isinstance(real_branch, RemoteBranch):
134
return RemoteBranch(self, self.find_repository(), real_branch)
157
result = RemoteBranch(self, self.find_repository(), real_branch)
160
# BzrDir.clone_on_transport() uses the result of create_branch but does
161
# not return it to its callers; we save approximately 8% of our round
162
# trips by handing the branch we created back to the first caller to
163
# open_branch rather than probing anew. Long term we need a API in
164
# bzrdir that doesn't discard result objects (like result_branch).
166
self._next_open_branch_result = result
138
169
def destroy_branch(self):
139
170
"""See BzrDir.destroy_branch"""
140
171
self._ensure_real()
141
172
self._real_bzrdir.destroy_branch()
173
self._next_open_branch_result = None
143
175
def create_workingtree(self, revision_id=None, from_branch=None):
144
176
raise errors.NotLocalUrl(self.transport.base)
180
217
# a branch reference, use the existing BranchReference logic.
181
218
format = BranchReferenceFormat()
182
219
return format.open(self, _found=True, location=reference_url)
221
def _open_repo_v1(self, path):
222
verb = 'BzrDir.find_repository'
223
response = self._call(verb, path)
224
if response[0] != 'ok':
225
raise errors.UnexpectedSmartServerResponse(response)
226
# servers that only support the v1 method don't support external
229
repo = self._real_bzrdir.open_repository()
230
response = response + ('no', repo._format.network_name())
231
return response, repo
233
def _open_repo_v2(self, path):
234
verb = 'BzrDir.find_repositoryV2'
235
response = self._call(verb, path)
236
if response[0] != 'ok':
237
raise errors.UnexpectedSmartServerResponse(response)
239
repo = self._real_bzrdir.open_repository()
240
response = response + (repo._format.network_name(),)
241
return response, repo
243
def _open_repo_v3(self, path):
244
verb = 'BzrDir.find_repositoryV3'
245
medium = self._client._medium
246
if medium._is_remote_before((1, 13)):
247
raise errors.UnknownSmartMethod(verb)
248
response = self._call(verb, path)
249
if response[0] != 'ok':
250
raise errors.UnexpectedSmartServerResponse(response)
251
return response, None
184
253
def open_repository(self):
185
254
path = self._path_for_remote_call(self._client)
186
verb = 'BzrDir.find_repositoryV2'
188
response = self._call(verb, path)
189
except errors.UnknownSmartMethod:
190
verb = 'BzrDir.find_repository'
191
response = self._call(verb, path)
256
for probe in [self._open_repo_v3, self._open_repo_v2,
259
response, real_repo = probe(path)
261
except errors.UnknownSmartMethod:
264
raise errors.UnknownSmartMethod('BzrDir.find_repository{3,2,}')
192
265
if response[0] != 'ok':
193
266
raise errors.UnexpectedSmartServerResponse(response)
194
if verb == 'BzrDir.find_repository':
195
# servers that don't support the V2 method don't support external
197
response = response + ('no', )
198
if not (len(response) == 5):
267
if len(response) != 6:
199
268
raise SmartProtocolError('incorrect response length %s' % (response,))
200
269
if response[1] == '':
201
format = RemoteRepositoryFormat()
202
format.rich_root_data = (response[2] == 'yes')
203
format.supports_tree_reference = (response[3] == 'yes')
204
# No wire format to check this yet.
205
format.supports_external_lookups = (response[4] == 'yes')
270
# repo is at this dir.
271
format = response_tuple_to_repo_format(response[2:])
206
272
# Used to support creating a real format instance when needed.
207
273
format._creating_bzrdir = self
208
return RemoteRepository(self, format)
274
remote_repo = RemoteRepository(self, format)
275
format._creating_repo = remote_repo
276
if real_repo is not None:
277
remote_repo._set_real_repository(real_repo)
210
280
raise errors.NoRepositoryPresent(self)
272
349
def __init__(self):
273
350
repository.RepositoryFormat.__init__(self)
274
351
self._custom_format = None
352
self._network_name = None
353
self._creating_bzrdir = None
276
def initialize(self, a_bzrdir, shared=False):
355
def _vfs_initialize(self, a_bzrdir, shared):
356
"""Helper for common code in initialize."""
277
357
if self._custom_format:
278
# This returns a custom instance - e.g. a pack repo, not a remote
280
return self._custom_format.initialize(a_bzrdir, shared=shared)
281
if not isinstance(a_bzrdir, RemoteBzrDir):
358
# Custom format requested
359
result = self._custom_format.initialize(a_bzrdir, shared=shared)
360
elif self._creating_bzrdir is not None:
361
# Use the format that the repository we were created to back
282
363
prior_repo = self._creating_bzrdir.open_repository()
283
364
prior_repo._ensure_real()
284
return prior_repo._real_repository._format.initialize(
365
result = prior_repo._real_repository._format.initialize(
285
366
a_bzrdir, shared=shared)
286
# delegate to a real object at this point (remoteBzrDir delegate to the
287
# repository format which would lead to infinite recursion).
288
a_bzrdir._ensure_real()
289
result = a_bzrdir._real_bzrdir.create_repository(shared=shared)
368
# assume that a_bzr is a RemoteBzrDir but the smart server didn't
369
# support remote initialization.
370
# We delegate to a real object at this point (as RemoteBzrDir
371
# delegate to the repository format which would lead to infinite
372
# recursion if we just called a_bzrdir.create_repository.
373
a_bzrdir._ensure_real()
374
result = a_bzrdir._real_bzrdir.create_repository(shared=shared)
290
375
if not isinstance(result, RemoteRepository):
291
376
return self.open(a_bzrdir)
380
def initialize(self, a_bzrdir, shared=False):
381
# Being asked to create on a non RemoteBzrDir:
382
if not isinstance(a_bzrdir, RemoteBzrDir):
383
return self._vfs_initialize(a_bzrdir, shared)
384
medium = a_bzrdir._client._medium
385
if medium._is_remote_before((1, 13)):
386
return self._vfs_initialize(a_bzrdir, shared)
387
# Creating on a remote bzr dir.
388
# 1) get the network name to use.
389
if self._custom_format:
390
network_name = self._custom_format.network_name()
392
# Select the current bzrlib default and ask for that.
393
reference_bzrdir_format = bzrdir.format_registry.get('default')()
394
reference_format = reference_bzrdir_format.repository_format
395
network_name = reference_format.network_name()
396
# 2) try direct creation via RPC
397
path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
398
verb = 'BzrDir.create_repository'
404
response = a_bzrdir._call(verb, path, network_name, shared_str)
405
except errors.UnknownSmartMethod:
406
# Fallback - use vfs methods
407
return self._vfs_initialize(a_bzrdir, shared)
409
# Turn the response into a RemoteRepository object.
410
format = response_tuple_to_repo_format(response[1:])
411
# Used to support creating a real format instance when needed.
412
format._creating_bzrdir = a_bzrdir
413
remote_repo = RemoteRepository(a_bzrdir, format)
414
format._creating_repo = remote_repo
295
417
def open(self, a_bzrdir):
296
418
if not isinstance(a_bzrdir, RemoteBzrDir):
297
419
raise AssertionError('%r is not a RemoteBzrDir' % (a_bzrdir,))
298
420
return a_bzrdir.open_repository()
422
def _ensure_real(self):
423
if self._custom_format is None:
424
self._custom_format = repository.network_format_registry.get(
428
def _fetch_order(self):
430
return self._custom_format._fetch_order
433
def _fetch_uses_deltas(self):
435
return self._custom_format._fetch_uses_deltas
438
def _fetch_reconcile(self):
440
return self._custom_format._fetch_reconcile
300
442
def get_format_description(self):
301
443
return 'bzr remote repository'
761
936
# We need to accumulate additional repositories here, to pass them in
762
937
# on various RPC's.
763
939
self._fallback_repositories.append(repository)
764
# They are also seen by the fallback repository. If it doesn't exist
765
# yet they'll be added then. This implicitly copies them.
940
# If self._real_repository was parameterised already (e.g. because a
941
# _real_branch had its get_stacked_on_url method called), then the
942
# repository to be added may already be in the _real_repositories list.
943
if self._real_repository is not None:
944
if repository not in self._real_repository._fallback_repositories:
945
self._real_repository.add_fallback_repository(repository)
947
# They are also seen by the fallback repository. If it doesn't
948
# exist yet they'll be added then. This implicitly copies them.
768
951
def add_inventory(self, revid, inv, parents):
769
952
self._ensure_real()
865
1048
self._ensure_real()
866
1049
return self._real_repository._get_versioned_file_checker(
867
1050
revisions, revision_versions_cache)
869
1052
def iter_files_bytes(self, desired_files):
870
1053
"""See Repository.iter_file_bytes.
872
1055
self._ensure_real()
873
1056
return self._real_repository.iter_files_bytes(desired_files)
876
def _fetch_order(self):
877
"""Decorate the real repository for now.
879
In the long term getting this back from the remote repository as part
880
of open would be more efficient.
883
return self._real_repository._fetch_order
886
def _fetch_uses_deltas(self):
887
"""Decorate the real repository for now.
889
In the long term getting this back from the remote repository as part
890
of open would be more efficient.
893
return self._real_repository._fetch_uses_deltas
896
def _fetch_reconcile(self):
897
"""Decorate the real repository for now.
899
In the long term getting this back from the remote repository as part
900
of open would be more efficient.
903
return self._real_repository._fetch_reconcile
905
1058
def get_parent_map(self, revision_ids):
906
1059
"""See bzrlib.Graph.get_parent_map()."""
907
1060
return self._make_parents_provider().get_parent_map(revision_ids)
1243
1406
raise errors.UnexpectedSmartServerResponse(response)
1409
class RemoteStreamSink(repository.StreamSink):
1411
def _insert_real(self, stream, src_format, resume_tokens):
1412
self.target_repo._ensure_real()
1413
sink = self.target_repo._real_repository._get_sink()
1414
result = sink.insert_stream(stream, src_format, resume_tokens)
1416
self.target_repo.autopack()
1419
def insert_stream(self, stream, src_format, resume_tokens):
1420
repo = self.target_repo
1421
client = repo._client
1422
medium = client._medium
1423
if medium._is_remote_before((1, 13)):
1424
# No possible way this can work.
1425
return self._insert_real(stream, src_format, resume_tokens)
1426
path = repo.bzrdir._path_for_remote_call(client)
1427
if not resume_tokens:
1428
# XXX: Ugly but important for correctness, *will* be fixed during
1429
# 1.13 cycle. Pushing a stream that is interrupted results in a
1430
# fallback to the _real_repositories sink *with a partial stream*.
1431
# Thats bad because we insert less data than bzr expected. To avoid
1432
# this we do a trial push to make sure the verb is accessible, and
1433
# do not fallback when actually pushing the stream. A cleanup patch
1434
# is going to look at rewinding/restarting the stream/partial
1436
byte_stream = smart_repo._stream_to_byte_stream([], src_format)
1438
response = client.call_with_body_stream(
1439
('Repository.insert_stream', path, ''), byte_stream)
1440
except errors.UnknownSmartMethod:
1441
medium._remember_remote_is_before((1,13))
1442
return self._insert_real(stream, src_format, resume_tokens)
1443
byte_stream = smart_repo._stream_to_byte_stream(
1445
resume_tokens = ' '.join(resume_tokens)
1446
response = client.call_with_body_stream(
1447
('Repository.insert_stream', path, resume_tokens), byte_stream)
1448
if response[0][0] not in ('ok', 'missing-basis'):
1449
raise errors.UnexpectedSmartServerResponse(response)
1450
if response[0][0] == 'missing-basis':
1451
tokens, missing_keys = bencode.bdecode_as_tuple(response[0][1])
1452
resume_tokens = tokens
1453
return resume_tokens, missing_keys
1455
if self.target_repo._real_repository is not None:
1456
collection = getattr(self.target_repo._real_repository,
1457
'_pack_collection', None)
1458
if collection is not None:
1459
collection.reload_pack_names()
1463
class RemoteStreamSource(repository.StreamSource):
1464
"""Stream data from a remote server."""
1466
def get_stream(self, search):
1467
# streaming with fallback repositories is not well defined yet: The
1468
# remote repository cannot see the fallback repositories, and thus
1469
# cannot satisfy the entire search in the general case. Likewise the
1470
# fallback repositories cannot reify the search to determine what they
1471
# should send. It likely needs a return value in the stream listing the
1472
# edge of the search to resume from in fallback repositories.
1473
if self.from_repository._fallback_repositories:
1474
return repository.StreamSource.get_stream(self, search)
1475
repo = self.from_repository
1476
client = repo._client
1477
medium = client._medium
1478
if medium._is_remote_before((1, 13)):
1479
# No possible way this can work.
1480
return repository.StreamSource.get_stream(self, search)
1481
path = repo.bzrdir._path_for_remote_call(client)
1483
recipe = repo._serialise_search_recipe(search._recipe)
1484
response = repo._call_with_body_bytes_expecting_body(
1485
'Repository.get_stream',
1486
(path, self.to_format.network_name()), recipe)
1487
response_tuple, response_handler = response
1488
except errors.UnknownSmartMethod:
1489
medium._remember_remote_is_before((1,13))
1490
return repository.StreamSource.get_stream(self, search)
1491
if response_tuple[0] != 'ok':
1492
raise errors.UnexpectedSmartServerResponse(response_tuple)
1493
byte_stream = response_handler.read_streamed_body()
1494
src_format, stream = smart_repo._byte_stream_to_stream(byte_stream)
1495
if src_format.network_name() != repo._format.network_name():
1496
raise AssertionError(
1497
"Mismatched RemoteRepository and stream src %r, %r" % (
1498
src_format.network_name(), repo._format.network_name()))
1246
1502
class RemoteBranchLockableFiles(LockableFiles):
1247
1503
"""A 'LockableFiles' implementation that talks to a smart server.
1249
1505
This is not a public interface class.
1269
1525
super(RemoteBranchFormat, self).__init__()
1270
1526
self._matchingbzrdir = RemoteBzrDirFormat()
1271
1527
self._matchingbzrdir.set_branch_format(self)
1528
self._custom_format = None
1273
1530
def __eq__(self, other):
1274
return (isinstance(other, RemoteBranchFormat) and
1531
return (isinstance(other, RemoteBranchFormat) and
1275
1532
self.__dict__ == other.__dict__)
1277
1534
def get_format_description(self):
1278
1535
return 'Remote BZR Branch'
1280
def get_format_string(self):
1281
return 'Remote BZR Branch'
1537
def network_name(self):
1538
return self._network_name
1283
1540
def open(self, a_bzrdir):
1284
1541
return a_bzrdir.open_branch()
1543
def _vfs_initialize(self, a_bzrdir):
1544
# Initialisation when using a local bzrdir object, or a non-vfs init
1545
# method is not available on the server.
1546
# self._custom_format is always set - the start of initialize ensures
1548
if isinstance(a_bzrdir, RemoteBzrDir):
1549
a_bzrdir._ensure_real()
1550
result = self._custom_format.initialize(a_bzrdir._real_bzrdir)
1552
# We assume the bzrdir is parameterised; it may not be.
1553
result = self._custom_format.initialize(a_bzrdir)
1554
if (isinstance(a_bzrdir, RemoteBzrDir) and
1555
not isinstance(result, RemoteBranch)):
1556
result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result)
1286
1559
def initialize(self, a_bzrdir):
1287
# Delegate to a _real object here - the RemoteBzrDir format now
1288
# supports delegating to parameterised branch formats and as such
1289
# this RemoteBranchFormat method is only called when no specific format
1560
# 1) get the network name to use.
1561
if self._custom_format:
1562
network_name = self._custom_format.network_name()
1564
# Select the current bzrlib default and ask for that.
1565
reference_bzrdir_format = bzrdir.format_registry.get('default')()
1566
reference_format = reference_bzrdir_format.get_branch_format()
1567
self._custom_format = reference_format
1568
network_name = reference_format.network_name()
1569
# Being asked to create on a non RemoteBzrDir:
1291
1570
if not isinstance(a_bzrdir, RemoteBzrDir):
1292
result = a_bzrdir.create_branch()
1571
return self._vfs_initialize(a_bzrdir)
1572
medium = a_bzrdir._client._medium
1573
if medium._is_remote_before((1, 13)):
1574
return self._vfs_initialize(a_bzrdir)
1575
# Creating on a remote bzr dir.
1576
# 2) try direct creation via RPC
1577
path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
1578
verb = 'BzrDir.create_branch'
1580
response = a_bzrdir._call(verb, path, network_name)
1581
except errors.UnknownSmartMethod:
1582
# Fallback - use vfs methods
1583
return self._vfs_initialize(a_bzrdir)
1584
if response[0] != 'ok':
1585
raise errors.UnexpectedSmartServerResponse(response)
1586
# Turn the response into a RemoteRepository object.
1587
format = RemoteBranchFormat()
1588
format._network_name = response[1]
1589
repo_format = response_tuple_to_repo_format(response[3:])
1590
if response[2] == '':
1591
repo_bzrdir = a_bzrdir
1294
a_bzrdir._ensure_real()
1295
result = a_bzrdir._real_bzrdir.create_branch()
1296
if not isinstance(result, RemoteBranch):
1297
result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result)
1593
repo_bzrdir = RemoteBzrDir(
1594
a_bzrdir.root_transport.clone(response[2]), a_bzrdir._format,
1596
remote_repo = RemoteRepository(repo_bzrdir, repo_format)
1597
remote_branch = RemoteBranch(a_bzrdir, remote_repo,
1598
format=format, setup_stacking=False)
1599
# XXX: We know this is a new branch, so it must have revno 0, revid
1600
# NULL_REVISION. Creating the branch locked would make this be unable
1601
# to be wrong; here its simply very unlikely to be wrong. RBC 20090225
1602
remote_branch._last_revision_info_cache = 0, NULL_REVISION
1603
return remote_branch
1300
1605
def supports_tags(self):
1301
1606
# Remote branches might support tags, but we won't know until we
1312
1617
def __init__(self, remote_bzrdir, remote_repository, real_branch=None,
1618
_client=None, format=None, setup_stacking=True):
1314
1619
"""Create a RemoteBranch instance.
1316
1621
:param real_branch: An optional local implementation of the branch
1317
1622
format, usually accessing the data via the VFS.
1318
1623
:param _client: Private parameter for testing.
1624
:param format: A RemoteBranchFormat object, None to create one
1625
automatically. If supplied it should have a network_name already
1627
:param setup_stacking: If True make an RPC call to determine the
1628
stacked (or not) status of the branch. If False assume the branch
1320
1631
# We intentionally don't call the parent class's __init__, because it
1321
1632
# will try to assign to self.tags, which is a property in this subclass.
1352
1662
self._repo_lock_token = None
1353
1663
self._lock_count = 0
1354
1664
self._leave_lock = False
1665
# Setup a format: note that we cannot call _ensure_real until all the
1666
# attributes above are set: This code cannot be moved higher up in this
1669
self._format = RemoteBranchFormat()
1670
if real_branch is not None:
1671
self._format._network_name = \
1672
self._real_branch._format.network_name()
1674
# # XXX: Need to get this from BzrDir.open_branch's return value.
1675
# self._ensure_real()
1676
# self._format._network_name = \
1677
# self._real_branch._format.network_name()
1679
self._format = format
1355
1680
# The base class init is not called, so we duplicate this:
1356
1681
hooks = branch.Branch.hooks['open']
1357
1682
for hook in hooks:
1359
self._setup_stacking()
1685
self._setup_stacking()
1361
1687
def _setup_stacking(self):
1362
1688
# configure stacking into the remote repository, by reading it from
1370
1696
fallback_url = urlutils.join(self.base, fallback_url)
1371
1697
transports = [self.bzrdir.root_transport]
1372
1698
if self._real_branch is not None:
1699
# The real repository is setup already:
1373
1700
transports.append(self._real_branch._transport)
1374
stacked_on = branch.Branch.open(fallback_url,
1375
possible_transports=transports)
1376
self.repository.add_fallback_repository(stacked_on.repository)
1701
self.repository.add_fallback_repository(
1702
self.repository._real_repository._fallback_repositories[0])
1704
stacked_on = branch.Branch.open(fallback_url,
1705
possible_transports=transports)
1706
self.repository.add_fallback_repository(stacked_on.repository)
1378
1708
def _get_real_transport(self):
1379
1709
# if we try vfs access, return the real branch's vfs transport
1702
2032
self._ensure_real()
1703
2033
return self._real_branch.set_stacked_on_url(stacked_location)
1705
def sprout(self, to_bzrdir, revision_id=None):
1706
branch_format = to_bzrdir._format._branch_format
1707
if (branch_format is None or
1708
isinstance(branch_format, RemoteBranchFormat)):
1709
# The to_bzrdir specifies RemoteBranchFormat (or no format, which
1710
# implies the same thing), but RemoteBranches can't be created at
1711
# arbitrary URLs. So create a branch in the same format as
1712
# _real_branch instead.
1713
# XXX: if to_bzrdir is a RemoteBzrDir, this should perhaps do
1714
# to_bzrdir.create_branch to create a RemoteBranch after all...
1716
result = self._real_branch._format.initialize(to_bzrdir)
1717
self.copy_content_into(result, revision_id=revision_id)
1718
result.set_parent(self.bzrdir.root_transport.base)
1720
result = branch.Branch.sprout(
1721
self, to_bzrdir, revision_id=revision_id)
1724
2035
@needs_write_lock
1725
2036
def pull(self, source, overwrite=False, stop_revision=None,
1797
2108
self._ensure_real()
1798
2109
return self._real_branch.set_push_location(location)
1801
def update_revisions(self, other, stop_revision=None, overwrite=False,
1803
"""See Branch.update_revisions."""
1806
if stop_revision is None:
1807
stop_revision = other.last_revision()
1808
if revision.is_null(stop_revision):
1809
# if there are no commits, we're done.
1811
self.fetch(other, stop_revision)
1814
# Just unconditionally set the new revision. We don't care if
1815
# the branches have diverged.
1816
self._set_last_revision(stop_revision)
1818
medium = self._client._medium
1819
if not medium._is_remote_before((1, 6)):
1821
self._set_last_revision_descendant(stop_revision, other)
1823
except errors.UnknownSmartMethod:
1824
medium._remember_remote_is_before((1, 6))
1825
# Fallback for pre-1.6 servers: check for divergence
1826
# client-side, then do _set_last_revision.
1827
last_rev = revision.ensure_null(self.last_revision())
1829
graph = self.repository.get_graph()
1830
if self._check_if_descendant_or_diverged(
1831
stop_revision, last_rev, graph, other):
1832
# stop_revision is a descendant of last_rev, but we aren't
1833
# overwriting, so we're done.
1835
self._set_last_revision(stop_revision)
1840
2112
def _extract_tar(tar, to_dir):
1841
2113
"""Extract all the contents of a tarfile object.