51
55
return self._client.call(method, *args)
52
56
except errors.ErrorFromSmartServer, err:
53
57
self._translate_error(err, **err_context)
55
59
def _call_expecting_body(self, method, *args, **err_context):
57
61
return self._client.call_expecting_body(method, *args)
58
62
except errors.ErrorFromSmartServer, err:
59
63
self._translate_error(err, **err_context)
61
65
def _call_with_body_bytes_expecting_body(self, method, args, body_bytes,
65
69
method, args, body_bytes)
66
70
except errors.ErrorFromSmartServer, err:
67
71
self._translate_error(err, **err_context)
74
def response_tuple_to_repo_format(response):
75
"""Convert a response tuple describing a repository format to a format."""
76
format = RemoteRepositoryFormat()
77
format.rich_root_data = (response[0] == 'yes')
78
format.supports_tree_reference = (response[1] == 'yes')
79
format.supports_external_lookups = (response[2] == 'yes')
80
format._network_name = response[3]
69
84
# Note: RemoteBzrDirFormat is in bzrdir.py
71
86
class RemoteBzrDir(BzrDir, _RpcHelper):
72
87
"""Control directory on a remote server, accessed via bzr:// or similar."""
74
def __init__(self, transport, _client=None):
89
def __init__(self, transport, format, _client=None):
75
90
"""Construct a RemoteBzrDir.
77
92
:param _client: Private parameter for testing. Disables probing and the
78
93
use of a real bzrdir.
80
BzrDir.__init__(self, transport, RemoteBzrDirFormat())
95
BzrDir.__init__(self, transport, format)
81
96
# this object holds a delegated bzrdir that uses file-level operations
82
97
# to talk to the other side
83
98
self._real_bzrdir = None
99
# 1-shot cache for the call pattern 'create_branch; open_branch' - see
100
# create_branch for details.
101
self._next_open_branch_result = None
85
103
if _client is None:
86
104
medium = transport.get_smart_medium()
108
126
def _translate_error(self, err, **context):
109
127
_translate_error(err, bzrdir=self, **context)
129
def break_lock(self):
130
# Prevent aliasing problems in the next_open_branch_result cache.
131
# See create_branch for rationale.
132
self._next_open_branch_result = None
133
return BzrDir.break_lock(self)
111
135
def cloning_metadir(self, stacked=False):
112
136
self._ensure_real()
113
137
return self._real_bzrdir.cloning_metadir(stacked)
115
139
def create_repository(self, shared=False):
117
self._real_bzrdir.create_repository(shared=shared)
118
return self.open_repository()
140
# as per meta1 formats - just delegate to the format object which may
142
result = self._format.repository_format.initialize(self, shared)
143
if not isinstance(result, RemoteRepository):
144
return self.open_repository()
120
148
def destroy_repository(self):
121
149
"""See BzrDir.destroy_repository"""
123
151
self._real_bzrdir.destroy_repository()
125
153
def create_branch(self):
127
real_branch = self._real_bzrdir.create_branch()
128
return RemoteBranch(self, self.find_repository(), real_branch)
154
# as per meta1 formats - just delegate to the format object which may
156
real_branch = self._format.get_branch_format().initialize(self)
157
if not isinstance(real_branch, RemoteBranch):
158
result = RemoteBranch(self, self.find_repository(), real_branch)
161
# BzrDir.clone_on_transport() uses the result of create_branch but does
162
# not return it to its callers; we save approximately 8% of our round
163
# trips by handing the branch we created back to the first caller to
164
# open_branch rather than probing anew. Long term we need a API in
165
# bzrdir that doesn't discard result objects (like result_branch).
167
self._next_open_branch_result = result
130
170
def destroy_branch(self):
131
171
"""See BzrDir.destroy_branch"""
132
172
self._ensure_real()
133
173
self._real_bzrdir.destroy_branch()
174
self._next_open_branch_result = None
135
176
def create_workingtree(self, revision_id=None, from_branch=None):
136
177
raise errors.NotLocalUrl(self.transport.base)
257
305
the attributes rich_root_data and supports_tree_reference are set
258
306
on a per instance basis, and are not set (and should not be) at
309
:ivar _custom_format: If set, a specific concrete repository format that
310
will be used when initializing a repository with this
311
RemoteRepositoryFormat.
312
:ivar _creating_repo: If set, the repository object that this
313
RemoteRepositoryFormat was created for: it can be called into
314
to obtain data like the network name.
262
317
_matchingbzrdir = RemoteBzrDirFormat()
264
def initialize(self, a_bzrdir, shared=False):
265
if not isinstance(a_bzrdir, RemoteBzrDir):
320
repository.RepositoryFormat.__init__(self)
321
self._custom_format = None
322
self._network_name = None
323
self._creating_bzrdir = None
325
def _vfs_initialize(self, a_bzrdir, shared):
326
"""Helper for common code in initialize."""
327
if self._custom_format:
328
# Custom format requested
329
result = self._custom_format.initialize(a_bzrdir, shared=shared)
330
elif self._creating_bzrdir is not None:
331
# Use the format that the repository we were created to back
266
333
prior_repo = self._creating_bzrdir.open_repository()
267
334
prior_repo._ensure_real()
268
return prior_repo._real_repository._format.initialize(
335
result = prior_repo._real_repository._format.initialize(
269
336
a_bzrdir, shared=shared)
270
return a_bzrdir.create_repository(shared=shared)
338
# assume that a_bzr is a RemoteBzrDir but the smart server didn't
339
# support remote initialization.
340
# We delegate to a real object at this point (as RemoteBzrDir
341
# delegate to the repository format which would lead to infinite
342
# recursion if we just called a_bzrdir.create_repository.
343
a_bzrdir._ensure_real()
344
result = a_bzrdir._real_bzrdir.create_repository(shared=shared)
345
if not isinstance(result, RemoteRepository):
346
return self.open(a_bzrdir)
350
def initialize(self, a_bzrdir, shared=False):
351
# Being asked to create on a non RemoteBzrDir:
352
if not isinstance(a_bzrdir, RemoteBzrDir):
353
return self._vfs_initialize(a_bzrdir, shared)
354
medium = a_bzrdir._client._medium
355
if medium._is_remote_before((1, 13)):
356
return self._vfs_initialize(a_bzrdir, shared)
357
# Creating on a remote bzr dir.
358
# 1) get the network name to use.
359
if self._custom_format:
360
network_name = self._custom_format.network_name()
362
# Select the current bzrlib default and ask for that.
363
reference_bzrdir_format = bzrdir.format_registry.get('default')()
364
reference_format = reference_bzrdir_format.repository_format
365
network_name = reference_format.network_name()
366
# 2) try direct creation via RPC
367
path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
368
verb = 'BzrDir.create_repository'
374
response = a_bzrdir._call(verb, path, network_name, shared_str)
375
except errors.UnknownSmartMethod:
376
# Fallback - use vfs methods
377
return self._vfs_initialize(a_bzrdir, shared)
379
# Turn the response into a RemoteRepository object.
380
format = response_tuple_to_repo_format(response[1:])
381
# Used to support creating a real format instance when needed.
382
format._creating_bzrdir = a_bzrdir
383
remote_repo = RemoteRepository(a_bzrdir, format)
384
format._creating_repo = remote_repo
272
387
def open(self, a_bzrdir):
273
388
if not isinstance(a_bzrdir, RemoteBzrDir):
274
389
raise AssertionError('%r is not a RemoteBzrDir' % (a_bzrdir,))
289
404
raise errors.BadConversionTarget(
290
405
'Does not support nested trees', target_format)
407
def network_name(self):
408
if self._network_name:
409
return self._network_name
410
self._creating_repo._ensure_real()
411
return self._creating_repo._real_repository._format.network_name()
414
def _serializer(self):
415
if self._custom_format is not None:
416
return self._custom_format._serializer
417
elif self._network_name is not None:
418
self._custom_format = repository.network_format_registry.get(
420
return self._custom_format._serializer
422
# We should only be getting asked for the serializer for
423
# RemoteRepositoryFormat objects when the RemoteRepositoryFormat object
424
# is a concrete instance for a RemoteRepository. In this case we know
425
# the creating_repo and can use it to supply the serializer.
426
self._creating_repo._ensure_real()
427
return self._creating_repo._real_repository._format._serializer
293
430
class RemoteRepository(_RpcHelper):
294
431
"""Repository accessed over rpc.
738
889
# We need to accumulate additional repositories here, to pass them in
739
890
# on various RPC's.
740
892
self._fallback_repositories.append(repository)
741
# They are also seen by the fallback repository. If it doesn't exist
742
# yet they'll be added then. This implicitly copies them.
893
# If self._real_repository was parameterised already (e.g. because a
894
# _real_branch had its get_stacked_on_url method called), then the
895
# repository to be added may already be in the _real_repositories list.
896
if self._real_repository is not None:
897
if repository not in self._real_repository._fallback_repositories:
898
self._real_repository.add_fallback_repository(repository)
900
# They are also seen by the fallback repository. If it doesn't
901
# exist yet they'll be added then. This implicitly copies them.
745
904
def add_inventory(self, revid, inv, parents):
746
905
self._ensure_real()
1220
1391
raise errors.UnexpectedSmartServerResponse(response)
1394
class RemoteStreamSink(repository.StreamSink):
1396
def __init__(self, target_repo):
1397
repository.StreamSink.__init__(self, target_repo)
1399
def _insert_real(self, stream, src_format, resume_tokens):
1400
self.target_repo._ensure_real()
1401
sink = self.target_repo._real_repository._get_sink()
1402
result = sink.insert_stream(stream, src_format, resume_tokens)
1404
self.target_repo.autopack()
1407
def insert_stream(self, stream, src_format, resume_tokens):
1408
repo = self.target_repo
1409
client = repo._client
1410
medium = client._medium
1411
if medium._is_remote_before((1, 13)):
1412
# No possible way this can work.
1413
return self._insert_real(stream, src_format, resume_tokens)
1414
path = repo.bzrdir._path_for_remote_call(client)
1415
if not resume_tokens:
1416
# XXX: Ugly but important for correctness, *will* be fixed during
1417
# 1.13 cycle. Pushing a stream that is interrupted results in a
1418
# fallback to the _real_repositories sink *with a partial stream*.
1419
# Thats bad because we insert less data than bzr expected. To avoid
1420
# this we do a trial push to make sure the verb is accessible, and
1421
# do not fallback when actually pushing the stream. A cleanup patch
1422
# is going to look at rewinding/restarting the stream/partial
1424
byte_stream = self._stream_to_byte_stream([], src_format)
1426
response = client.call_with_body_stream(
1427
('Repository.insert_stream', path, ''), byte_stream)
1428
except errors.UnknownSmartMethod:
1429
medium._remember_remote_is_before((1,13))
1430
return self._insert_real(stream, src_format, resume_tokens)
1431
byte_stream = self._stream_to_byte_stream(stream, src_format)
1432
resume_tokens = ' '.join(resume_tokens)
1433
response = client.call_with_body_stream(
1434
('Repository.insert_stream', path, resume_tokens), byte_stream)
1435
if response[0][0] not in ('ok', 'missing-basis'):
1436
raise errors.UnexpectedSmartServerResponse(response)
1437
if response[0][0] == 'missing-basis':
1438
tokens, missing_keys = bencode.bdecode_as_tuple(response[0][1])
1439
resume_tokens = tokens
1440
return resume_tokens, missing_keys
1442
if self.target_repo._real_repository is not None:
1443
collection = getattr(self.target_repo._real_repository,
1444
'_pack_collection', None)
1445
if collection is not None:
1446
collection.reload_pack_names()
1449
def _stream_to_byte_stream(self, stream, src_format):
1451
pack_writer = pack.ContainerWriter(bytes.append)
1453
pack_writer.add_bytes_record(src_format.network_name(), '')
1455
def get_adapter(adapter_key):
1457
return adapters[adapter_key]
1459
adapter_factory = adapter_registry.get(adapter_key)
1460
adapter = adapter_factory(self)
1461
adapters[adapter_key] = adapter
1463
for substream_type, substream in stream:
1464
for record in substream:
1465
if record.storage_kind in ('chunked', 'fulltext'):
1466
serialised = record_to_fulltext_bytes(record)
1468
serialised = record.get_bytes_as(record.storage_kind)
1469
pack_writer.add_bytes_record(serialised, [(substream_type,)])
1223
1478
class RemoteBranchLockableFiles(LockableFiles):
1224
1479
"""A 'LockableFiles' implementation that talks to a smart server.
1226
1481
This is not a public interface class.
1246
1501
super(RemoteBranchFormat, self).__init__()
1247
1502
self._matchingbzrdir = RemoteBzrDirFormat()
1248
1503
self._matchingbzrdir.set_branch_format(self)
1504
self._custom_format = None
1250
1506
def __eq__(self, other):
1251
return (isinstance(other, RemoteBranchFormat) and
1507
return (isinstance(other, RemoteBranchFormat) and
1252
1508
self.__dict__ == other.__dict__)
1254
1510
def get_format_description(self):
1255
1511
return 'Remote BZR Branch'
1257
def get_format_string(self):
1258
return 'Remote BZR Branch'
1513
def network_name(self):
1514
return self._network_name
1260
1516
def open(self, a_bzrdir):
1261
1517
return a_bzrdir.open_branch()
1519
def _vfs_initialize(self, a_bzrdir):
1520
# Initialisation when using a local bzrdir object, or a non-vfs init
1521
# method is not available on the server.
1522
# self._custom_format is always set - the start of initialize ensures
1524
if isinstance(a_bzrdir, RemoteBzrDir):
1525
a_bzrdir._ensure_real()
1526
result = self._custom_format.initialize(a_bzrdir._real_bzrdir)
1528
# We assume the bzrdir is parameterised; it may not be.
1529
result = self._custom_format.initialize(a_bzrdir)
1530
if (isinstance(a_bzrdir, RemoteBzrDir) and
1531
not isinstance(result, RemoteBranch)):
1532
result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result)
1263
1535
def initialize(self, a_bzrdir):
1264
return a_bzrdir.create_branch()
1536
# 1) get the network name to use.
1537
if self._custom_format:
1538
network_name = self._custom_format.network_name()
1540
# Select the current bzrlib default and ask for that.
1541
reference_bzrdir_format = bzrdir.format_registry.get('default')()
1542
reference_format = reference_bzrdir_format.get_branch_format()
1543
self._custom_format = reference_format
1544
network_name = reference_format.network_name()
1545
# Being asked to create on a non RemoteBzrDir:
1546
if not isinstance(a_bzrdir, RemoteBzrDir):
1547
return self._vfs_initialize(a_bzrdir)
1548
medium = a_bzrdir._client._medium
1549
if medium._is_remote_before((1, 13)):
1550
return self._vfs_initialize(a_bzrdir)
1551
# Creating on a remote bzr dir.
1552
# 2) try direct creation via RPC
1553
path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
1554
verb = 'BzrDir.create_branch'
1556
response = a_bzrdir._call(verb, path, network_name)
1557
except errors.UnknownSmartMethod:
1558
# Fallback - use vfs methods
1559
return self._vfs_initialize(a_bzrdir)
1560
if response[0] != 'ok':
1561
raise errors.UnexpectedSmartServerResponse(response)
1562
# Turn the response into a RemoteRepository object.
1563
format = RemoteBranchFormat()
1564
format._network_name = response[1]
1565
repo_format = response_tuple_to_repo_format(response[3:])
1566
if response[2] == '':
1567
repo_bzrdir = a_bzrdir
1569
repo_bzrdir = RemoteBzrDir(
1570
a_bzrdir.root_transport.clone(response[2]), a_bzrdir._format,
1572
remote_repo = RemoteRepository(repo_bzrdir, repo_format)
1573
remote_branch = RemoteBranch(a_bzrdir, remote_repo,
1574
format=format, setup_stacking=False)
1575
# XXX: We know this is a new branch, so it must have revno 0, revid
1576
# NULL_REVISION. Creating the branch locked would make this be unable
1577
# to be wrong; here its simply very unlikely to be wrong. RBC 20090225
1578
remote_branch._last_revision_info_cache = 0, NULL_REVISION
1579
return remote_branch
1266
1581
def supports_tags(self):
1267
1582
# Remote branches might support tags, but we won't know until we
1278
1593
def __init__(self, remote_bzrdir, remote_repository, real_branch=None,
1594
_client=None, format=None, setup_stacking=True):
1280
1595
"""Create a RemoteBranch instance.
1282
1597
:param real_branch: An optional local implementation of the branch
1283
1598
format, usually accessing the data via the VFS.
1284
1599
:param _client: Private parameter for testing.
1600
:param format: A RemoteBranchFormat object, None to create one
1601
automatically. If supplied it should have a network_name already
1603
:param setup_stacking: If True make an RPC call to determine the
1604
stacked (or not) status of the branch. If False assume the branch
1286
1607
# We intentionally don't call the parent class's __init__, because it
1287
1608
# will try to assign to self.tags, which is a property in this subclass.
1318
1638
self._repo_lock_token = None
1319
1639
self._lock_count = 0
1320
1640
self._leave_lock = False
1641
# Setup a format: note that we cannot call _ensure_real until all the
1642
# attributes above are set: This code cannot be moved higher up in this
1645
self._format = RemoteBranchFormat()
1646
if real_branch is not None:
1647
self._format._network_name = \
1648
self._real_branch._format.network_name()
1650
# # XXX: Need to get this from BzrDir.open_branch's return value.
1651
# self._ensure_real()
1652
# self._format._network_name = \
1653
# self._real_branch._format.network_name()
1655
self._format = format
1321
1656
# The base class init is not called, so we duplicate this:
1322
1657
hooks = branch.Branch.hooks['open']
1323
1658
for hook in hooks:
1325
self._setup_stacking()
1661
self._setup_stacking()
1327
1663
def _setup_stacking(self):
1328
1664
# configure stacking into the remote repository, by reading it from
1336
1672
fallback_url = urlutils.join(self.base, fallback_url)
1337
1673
transports = [self.bzrdir.root_transport]
1338
1674
if self._real_branch is not None:
1675
# The real repository is setup already:
1339
1676
transports.append(self._real_branch._transport)
1340
stacked_on = branch.Branch.open(fallback_url,
1341
possible_transports=transports)
1342
self.repository.add_fallback_repository(stacked_on.repository)
1677
self.repository.add_fallback_repository(
1678
self.repository._real_repository._fallback_repositories[0])
1680
stacked_on = branch.Branch.open(fallback_url,
1681
possible_transports=transports)
1682
self.repository.add_fallback_repository(stacked_on.repository)
1344
1684
def _get_real_transport(self):
1345
1685
# if we try vfs access, return the real branch's vfs transport
1581
1921
def _set_last_revision_descendant(self, revision_id, other_branch,
1582
1922
allow_diverged=False, allow_overwrite_descendant=False):
1923
# This performs additional work to meet the hook contract; while its
1924
# undesirable, we have to synthesise the revno to call the hook, and
1925
# not calling the hook is worse as it means changes can't be prevented.
1926
# Having calculated this though, we can't just call into
1927
# set_last_revision_info as a simple call, because there is a set_rh
1928
# hook that some folk may still be using.
1929
old_revno, old_revid = self.last_revision_info()
1930
history = self._lefthand_history(revision_id)
1931
self._run_pre_change_branch_tip_hooks(len(history), revision_id)
1583
1932
err_context = {'other_branch': other_branch}
1584
1933
response = self._call('Branch.set_last_revision_ex',
1585
1934
self._remote_path(), self._lock_token, self._repo_lock_token,
1590
1939
raise errors.UnexpectedSmartServerResponse(response)
1591
1940
new_revno, new_revision_id = response[1:]
1592
1941
self._last_revision_info_cache = new_revno, new_revision_id
1942
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
1593
1943
if self._real_branch is not None:
1594
1944
cache = new_revno, new_revision_id
1595
1945
self._real_branch._last_revision_info_cache = cache
1597
1947
def _set_last_revision(self, revision_id):
1948
old_revno, old_revid = self.last_revision_info()
1949
# This performs additional work to meet the hook contract; while its
1950
# undesirable, we have to synthesise the revno to call the hook, and
1951
# not calling the hook is worse as it means changes can't be prevented.
1952
# Having calculated this though, we can't just call into
1953
# set_last_revision_info as a simple call, because there is a set_rh
1954
# hook that some folk may still be using.
1955
history = self._lefthand_history(revision_id)
1956
self._run_pre_change_branch_tip_hooks(len(history), revision_id)
1598
1957
self._clear_cached_state()
1599
1958
response = self._call('Branch.set_last_revision',
1600
1959
self._remote_path(), self._lock_token, self._repo_lock_token,
1602
1961
if response != ('ok',):
1603
1962
raise errors.UnexpectedSmartServerResponse(response)
1963
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
1605
1965
@needs_write_lock
1606
1966
def set_revision_history(self, rev_history):
1613
1973
rev_id = rev_history[-1]
1614
1974
self._set_last_revision(rev_id)
1975
for hook in branch.Branch.hooks['set_rh']:
1976
hook(self, rev_history)
1615
1977
self._cache_revision_history(rev_history)
1617
1979
def get_parent(self):
1618
1980
self._ensure_real()
1619
1981
return self._real_branch.get_parent()
1983
def _get_parent_location(self):
1984
# Used by tests, when checking normalisation of given vs stored paths.
1986
return self._real_branch._get_parent_location()
1621
1988
def set_parent(self, url):
1622
1989
self._ensure_real()
1623
1990
return self._real_branch.set_parent(url)
1992
def _set_parent_location(self, url):
1993
# Used by tests, to poke bad urls into branch configurations
1995
self.set_parent(url)
1998
return self._real_branch._set_parent_location(url)
1625
2000
def set_stacked_on_url(self, stacked_location):
1626
2001
"""Set the URL this branch is stacked against.
1633
2008
self._ensure_real()
1634
2009
return self._real_branch.set_stacked_on_url(stacked_location)
1636
def sprout(self, to_bzrdir, revision_id=None):
1637
branch_format = to_bzrdir._format._branch_format
1638
if (branch_format is None or
1639
isinstance(branch_format, RemoteBranchFormat)):
1640
# The to_bzrdir specifies RemoteBranchFormat (or no format, which
1641
# implies the same thing), but RemoteBranches can't be created at
1642
# arbitrary URLs. So create a branch in the same format as
1643
# _real_branch instead.
1644
# XXX: if to_bzrdir is a RemoteBzrDir, this should perhaps do
1645
# to_bzrdir.create_branch to create a RemoteBranch after all...
1647
result = self._real_branch._format.initialize(to_bzrdir)
1648
self.copy_content_into(result, revision_id=revision_id)
1649
result.set_parent(self.bzrdir.root_transport.base)
1651
result = branch.Branch.sprout(
1652
self, to_bzrdir, revision_id=revision_id)
1655
2011
@needs_write_lock
1656
2012
def pull(self, source, overwrite=False, stop_revision=None,
1724
2084
self._ensure_real()
1725
2085
return self._real_branch.set_push_location(location)
1728
def update_revisions(self, other, stop_revision=None, overwrite=False,
1730
"""See Branch.update_revisions."""
1733
if stop_revision is None:
1734
stop_revision = other.last_revision()
1735
if revision.is_null(stop_revision):
1736
# if there are no commits, we're done.
1738
self.fetch(other, stop_revision)
1741
# Just unconditionally set the new revision. We don't care if
1742
# the branches have diverged.
1743
self._set_last_revision(stop_revision)
1745
medium = self._client._medium
1746
if not medium._is_remote_before((1, 6)):
1748
self._set_last_revision_descendant(stop_revision, other)
1750
except errors.UnknownSmartMethod:
1751
medium._remember_remote_is_before((1, 6))
1752
# Fallback for pre-1.6 servers: check for divergence
1753
# client-side, then do _set_last_revision.
1754
last_rev = revision.ensure_null(self.last_revision())
1756
graph = self.repository.get_graph()
1757
if self._check_if_descendant_or_diverged(
1758
stop_revision, last_rev, graph, other):
1759
# stop_revision is a descendant of last_rev, but we aren't
1760
# overwriting, so we're done.
1762
self._set_last_revision(stop_revision)
1767
2088
def _extract_tar(tar, to_dir):
1768
2089
"""Extract all the contents of a tarfile object.