1
# Copyright (C) 2006, 2007, 2008, 2009 Canonical Ltd
1
# Copyright (C) 2006, 2007, 2008 Canonical Ltd
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
# TODO: At some point, handle upgrades by just passing the whole request
18
# across to run on the server.
19
22
from bzrlib import (
31
33
revision as _mod_revision,
34
37
from bzrlib.branch import BranchReferenceFormat
35
38
from bzrlib.bzrdir import BzrDir, RemoteBzrDirFormat
42
45
from bzrlib.smart import client, vfs, repository as smart_repo
43
46
from bzrlib.revision import ensure_null, NULL_REVISION
44
47
from bzrlib.trace import mutter, note, warning
48
from bzrlib.util import bencode
47
51
class _RpcHelper(object):
59
63
except errors.ErrorFromSmartServer, err:
60
64
self._translate_error(err, **err_context)
62
def _call_with_body_bytes(self, method, args, body_bytes, **err_context):
64
return self._client.call_with_body_bytes(method, args, body_bytes)
65
except errors.ErrorFromSmartServer, err:
66
self._translate_error(err, **err_context)
68
66
def _call_with_body_bytes_expecting_body(self, method, args, body_bytes,
677
670
self._ensure_real()
678
671
return self._real_repository.suspend_write_group()
680
def get_missing_parent_inventories(self, check_for_missing_texts=True):
682
return self._real_repository.get_missing_parent_inventories(
683
check_for_missing_texts=check_for_missing_texts)
685
def _get_rev_id_for_revno_vfs(self, revno, known_pair):
687
return self._real_repository.get_rev_id_for_revno(
690
def get_rev_id_for_revno(self, revno, known_pair):
691
"""See Repository.get_rev_id_for_revno."""
692
path = self.bzrdir._path_for_remote_call(self._client)
694
if self._client._medium._is_remote_before((1, 17)):
695
return self._get_rev_id_for_revno_vfs(revno, known_pair)
696
response = self._call(
697
'Repository.get_rev_id_for_revno', path, revno, known_pair)
698
except errors.UnknownSmartMethod:
699
self._client._medium._remember_remote_is_before((1, 17))
700
return self._get_rev_id_for_revno_vfs(revno, known_pair)
701
if response[0] == 'ok':
702
return True, response[1]
703
elif response[0] == 'history-incomplete':
704
known_pair = response[1:3]
705
for fallback in self._fallback_repositories:
706
found, result = fallback.get_rev_id_for_revno(revno, known_pair)
711
# Not found in any fallbacks
712
return False, known_pair
714
raise errors.UnexpectedSmartServerResponse(response)
673
def get_missing_parent_inventories(self):
675
return self._real_repository.get_missing_parent_inventories()
716
677
def _ensure_real(self):
717
678
"""Ensure that there is a _real_repository set.
727
688
invocation. If in doubt chat to the bzr network team.
729
690
if self._real_repository is None:
730
if 'hpssvfs' in debug.debug_flags:
732
warning('VFS Repository access triggered\n%s',
733
''.join(traceback.format_stack()))
734
691
self._unstacked_provider.missing_keys.clear()
735
692
self.bzrdir._ensure_real()
736
693
self._set_real_repository(
817
774
result.add(_mod_revision.NULL_REVISION)
820
def _has_same_fallbacks(self, other_repo):
821
"""Returns true if the repositories have the same fallbacks."""
822
# XXX: copied from Repository; it should be unified into a base class
823
# <https://bugs.edge.launchpad.net/bzr/+bug/401622>
824
my_fb = self._fallback_repositories
825
other_fb = other_repo._fallback_repositories
826
if len(my_fb) != len(other_fb):
828
for f, g in zip(my_fb, other_fb):
829
if not f.has_same_location(g):
833
777
def has_same_location(self, other):
834
# TODO: Move to RepositoryBase and unify with the regular Repository
835
# one; unfortunately the tests rely on slightly different behaviour at
836
# present -- mbp 20090710
837
778
return (self.__class__ is other.__class__ and
838
779
self.bzrdir.transport.base == other.bzrdir.transport.base)
915
856
self._unstacked_provider.enable_cache(cache_misses=True)
916
857
if self._real_repository is not None:
917
858
self._real_repository.lock_read()
918
for repo in self._fallback_repositories:
921
860
self._lock_count += 1
861
for repo in self._fallback_repositories:
923
864
def _remote_lock_write(self, token):
924
865
path = self.bzrdir._path_for_remote_call(self._client)
956
897
self._lock_count = 1
957
898
cache_misses = self._real_repository is None
958
899
self._unstacked_provider.enable_cache(cache_misses=cache_misses)
959
for repo in self._fallback_repositories:
960
# Writes don't affect fallback repos
962
900
elif self._lock_mode == 'r':
963
901
raise errors.ReadOnlyError(self)
965
903
self._lock_count += 1
904
for repo in self._fallback_repositories:
905
# Writes don't affect fallback repos
966
907
return self._lock_token or None
968
909
def leave_lock_in_place(self):
1143
1080
# We need to accumulate additional repositories here, to pass them in
1144
1081
# on various RPC's.
1146
if self.is_locked():
1147
# We will call fallback.unlock() when we transition to the unlocked
1148
# state, so always add a lock here. If a caller passes us a locked
1149
# repository, they are responsible for unlocking it later.
1150
repository.lock_read()
1151
1083
self._fallback_repositories.append(repository)
1152
1084
# If self._real_repository was parameterised already (e.g. because a
1153
1085
# _real_branch had its get_stacked_on_url method called), then the
1250
1182
raise errors.InternalBzrError(
1251
1183
"May not fetch while in a write group.")
1252
1184
# fast path same-url fetch operations
1253
if (self.has_same_location(source)
1254
and fetch_spec is None
1255
and self._has_same_fallbacks(source)):
1185
if self.has_same_location(source) and fetch_spec is None:
1256
1186
# check that last_revision is in 'from' and then return a
1257
1187
# no-operation.
1258
1188
if (revision_id is not None and
1470
1400
return self._real_repository.get_revision_reconcile(revision_id)
1472
1402
@needs_read_lock
1473
def check(self, revision_ids=None, callback_refs=None, check_repo=True):
1403
def check(self, revision_ids=None):
1474
1404
self._ensure_real()
1475
return self._real_repository.check(revision_ids=revision_ids,
1476
callback_refs=callback_refs, check_repo=check_repo)
1405
return self._real_repository.check(revision_ids=revision_ids)
1478
1407
def copy_content_into(self, destination, revision_id=None):
1479
1408
self._ensure_real()
1519
1448
return self._real_repository.inventories
1521
1450
@needs_write_lock
1522
def pack(self, hint=None):
1523
1452
"""Compress the data within the repository.
1525
1454
This is not currently implemented within the smart server.
1527
1456
self._ensure_real()
1528
return self._real_repository.pack(hint=hint)
1457
return self._real_repository.pack()
1531
1460
def revisions(self):
1619
1548
self._ensure_real()
1620
1549
return self._real_repository.revision_graph_can_have_wrong_parents()
1622
def _find_inconsistent_revision_parents(self, revisions_iterator=None):
1551
def _find_inconsistent_revision_parents(self):
1623
1552
self._ensure_real()
1624
return self._real_repository._find_inconsistent_revision_parents(
1553
return self._real_repository._find_inconsistent_revision_parents()
1627
1555
def _check_for_inconsistent_revision_parents(self):
1628
1556
self._ensure_real()
1634
1562
providers.insert(0, other)
1635
1563
providers.extend(r._make_parents_provider() for r in
1636
1564
self._fallback_repositories)
1637
return graph.StackedParentsProvider(providers)
1565
return graph._StackedParentsProvider(providers)
1639
1567
def _serialise_search_recipe(self, recipe):
1640
1568
"""Serialise a graph search recipe.
1735
1663
if (self.from_repository._fallback_repositories and
1736
1664
self.to_format._fetch_order == 'topological'):
1737
1665
return self._real_stream(self.from_repository, search)
1740
repos = [self.from_repository]
1746
repos.extend(repo._fallback_repositories)
1747
sources.append(repo)
1748
return self.missing_parents_chain(search, sources)
1666
return self.missing_parents_chain(search, [self.from_repository] +
1667
self.from_repository._fallback_repositories)
1750
1669
def _real_stream(self, repo, search):
1751
1670
"""Get a stream for search from repo.
1962
1881
self._ensure_real()
1963
1882
return self._custom_format.supports_stacking()
1965
def supports_set_append_revisions_only(self):
1967
return self._custom_format.supports_set_append_revisions_only()
1970
1885
class RemoteBranch(branch.Branch, _RpcHelper):
1971
1886
"""Branch stored on a server accessed by HPSS RPC.
1990
1905
# We intentionally don't call the parent class's __init__, because it
1991
1906
# will try to assign to self.tags, which is a property in this subclass.
1992
1907
# And the parent's __init__ doesn't do much anyway.
1908
self._revision_id_to_revno_cache = None
1909
self._partial_revision_id_to_revno_cache = {}
1910
self._revision_history_cache = None
1911
self._last_revision_info_cache = None
1912
self._merge_sorted_revisions_cache = None
1993
1913
self.bzrdir = remote_bzrdir
1994
1914
if _client is not None:
1995
1915
self._client = _client
2049
1967
except (errors.NotStacked, errors.UnstackableBranchFormat,
2050
1968
errors.UnstackableRepositoryFormat), e:
2052
self._is_stacked = True
2053
self._activate_fallback_location(fallback_url)
1970
self._activate_fallback_location(fallback_url, None)
2055
1972
def _get_config(self):
2056
1973
return RemoteBranchConfig(self)
2157
2074
raise errors.UnexpectedSmartServerResponse(response)
2158
2075
return response[1]
2160
def set_stacked_on_url(self, url):
2161
branch.Branch.set_stacked_on_url(self, url)
2163
self._is_stacked = False
2165
self._is_stacked = True
2167
2077
def _vfs_get_tags_bytes(self):
2168
2078
self._ensure_real()
2169
2079
return self._real_branch._get_tags_bytes()
2179
2089
return self._vfs_get_tags_bytes()
2180
2090
return response[0]
2182
def _vfs_set_tags_bytes(self, bytes):
2184
return self._real_branch._set_tags_bytes(bytes)
2186
def _set_tags_bytes(self, bytes):
2187
medium = self._client._medium
2188
if medium._is_remote_before((1, 18)):
2189
self._vfs_set_tags_bytes(bytes)
2192
self._remote_path(), self._lock_token, self._repo_lock_token)
2193
response = self._call_with_body_bytes(
2194
'Branch.set_tags_bytes', args, bytes)
2195
except errors.UnknownSmartMethod:
2196
medium._remember_remote_is_before((1, 18))
2197
self._vfs_set_tags_bytes(bytes)
2199
2092
def lock_read(self):
2200
2093
self.repository.lock_read()
2201
2094
if not self._lock_mode:
2255
2148
self.repository.lock_write(self._repo_lock_token)
2256
2149
return self._lock_token or None
2151
def _set_tags_bytes(self, bytes):
2153
return self._real_branch._set_tags_bytes(bytes)
2258
2155
def _unlock(self, branch_token, repo_token):
2259
2156
err_context = {'token': str((branch_token, repo_token))}
2260
2157
response = self._call(
2309
2206
raise NotImplementedError(self.dont_leave_lock_in_place)
2310
2207
self._leave_lock = False
2312
def get_rev_id(self, revno, history=None):
2314
return _mod_revision.NULL_REVISION
2315
last_revision_info = self.last_revision_info()
2316
ok, result = self.repository.get_rev_id_for_revno(
2317
revno, last_revision_info)
2320
missing_parent = result[1]
2321
# Either the revision named by the server is missing, or its parent
2322
# is. Call get_parent_map to determine which, so that we report a
2324
parent_map = self.repository.get_parent_map([missing_parent])
2325
if missing_parent in parent_map:
2326
missing_parent = parent_map[missing_parent]
2327
raise errors.RevisionNotPresent(missing_parent, self.repository)
2329
2209
def _last_revision_info(self):
2330
2210
response = self._call('Branch.last_revision_info', self._remote_path())
2331
2211
if response[0] != 'ok':
2337
2217
def _gen_revision_history(self):
2338
2218
"""See Branch._gen_revision_history()."""
2339
if self._is_stacked:
2341
return self._real_branch._gen_revision_history()
2342
2219
response_tuple, response_handler = self._call_expecting_body(
2343
2220
'Branch.revision_history', self._remote_path())
2344
2221
if response_tuple[0] != 'ok':