41
41
SmartProtocolError,
43
from bzrlib.i18n import gettext
44
from bzrlib.inventory import Inventory
43
45
from bzrlib.lockable_files import LockableFiles
44
46
from bzrlib.smart import client, vfs, repository as smart_repo
45
47
from bzrlib.smart.client import _SmartClient
46
48
from bzrlib.revision import NULL_REVISION
49
from bzrlib.revisiontree import InventoryRevisionTree
47
50
from bzrlib.repository import RepositoryWriteLockResult, _LazyListJoin
48
51
from bzrlib.trace import mutter, note, warning
54
_DEFAULT_SEARCH_DEPTH = 100
51
57
class _RpcHelper(object):
52
58
"""Mixin class that helps with issuing RPCs."""
327
333
_mod_bzrdir.BzrDirMetaFormat1._set_repository_format) #.im_func)
336
class RemoteControlStore(config.IniFileStore):
337
"""Control store which attempts to use HPSS calls to retrieve control store.
339
Note that this is specific to bzr-based formats.
342
def __init__(self, bzrdir):
343
super(RemoteControlStore, self).__init__()
345
self._real_store = None
347
def lock_write(self, token=None):
349
return self._real_store.lock_write(token)
353
return self._real_store.unlock()
357
# We need to be able to override the undecorated implementation
358
self.save_without_locking()
360
def save_without_locking(self):
361
super(RemoteControlStore, self).save()
363
def _ensure_real(self):
364
self.bzrdir._ensure_real()
365
if self._real_store is None:
366
self._real_store = config.ControlStore(self.bzrdir)
368
def external_url(self):
369
return self.bzrdir.user_url
371
def _load_content(self):
372
medium = self.bzrdir._client._medium
373
path = self.bzrdir._path_for_remote_call(self.bzrdir._client)
375
response, handler = self.bzrdir._call_expecting_body(
376
'BzrDir.get_config_file', path)
377
except errors.UnknownSmartMethod:
379
return self._real_store._load_content()
380
if len(response) and response[0] != 'ok':
381
raise errors.UnexpectedSmartServerResponse(response)
382
return handler.read_body_bytes()
384
def _save_content(self, content):
385
# FIXME JRV 2011-11-22: Ideally this should use a
386
# HPSS call too, but at the moment it is not possible
387
# to write lock control directories.
389
return self._real_store._save_content(content)
330
392
class RemoteBzrDir(_mod_bzrdir.BzrDir, _RpcHelper):
331
393
"""Control directory on a remote server, accessed via bzr:// or similar."""
481
543
def destroy_repository(self):
482
544
"""See BzrDir.destroy_repository"""
484
self._real_bzrdir.destroy_repository()
545
path = self._path_for_remote_call(self._client)
547
response = self._call('BzrDir.destroy_repository', path)
548
except errors.UnknownSmartMethod:
550
self._real_bzrdir.destroy_repository()
552
if response[0] != 'ok':
553
raise SmartProtocolError('unexpected response code %s' % (response,))
486
def create_branch(self, name=None, repository=None):
555
def create_branch(self, name=None, repository=None,
556
append_revisions_only=None):
487
557
# as per meta1 formats - just delegate to the format object which may
488
558
# be parameterised.
489
559
real_branch = self._format.get_branch_format().initialize(self,
490
name=name, repository=repository)
560
name=name, repository=repository,
561
append_revisions_only=append_revisions_only)
491
562
if not isinstance(real_branch, RemoteBranch):
492
563
if not isinstance(repository, RemoteRepository):
493
564
raise AssertionError(
658
729
def has_workingtree(self):
659
730
if self._has_working_tree is None:
661
self._has_working_tree = self._real_bzrdir.has_workingtree()
731
path = self._path_for_remote_call(self._client)
733
response = self._call('BzrDir.has_workingtree', path)
734
except errors.UnknownSmartMethod:
736
self._has_working_tree = self._real_bzrdir.has_workingtree()
738
if response[0] not in ('yes', 'no'):
739
raise SmartProtocolError('unexpected response code %s' % (response,))
740
self._has_working_tree = (response[0] == 'yes')
662
741
return self._has_working_tree
664
743
def open_workingtree(self, recommend_upgrade=True):
670
749
def _path_for_remote_call(self, client):
671
750
"""Return the path to be used for this bzrdir in a remote call."""
672
return client.remote_path_from_transport(self.root_transport)
751
return urlutils.split_segment_parameters_raw(
752
client.remote_path_from_transport(self.root_transport))[0]
674
754
def get_branch_transport(self, branch_format, name=None):
675
755
self._ensure_real()
691
771
"""Upgrading of remote bzrdirs is not supported yet."""
694
def clone(self, url, revision_id=None, force_new_repo=False,
695
preserve_stacking=False):
697
return self._real_bzrdir.clone(url, revision_id=revision_id,
698
force_new_repo=force_new_repo, preserve_stacking=preserve_stacking)
700
774
def _get_config(self):
701
775
return RemoteBzrDirConfig(self)
777
def _get_config_store(self):
778
return RemoteControlStore(self)
704
781
class RemoteRepositoryFormat(vf_repository.VersionedFileRepositoryFormat):
705
782
"""Format for repositories accessed over a _SmartClient.
735
812
self._supports_external_lookups = None
736
813
self._supports_tree_reference = None
737
814
self._supports_funky_characters = None
815
self._supports_nesting_repositories = None
738
816
self._rich_root_data = None
740
818
def __repr__(self):
777
855
return self._supports_funky_characters
858
def supports_nesting_repositories(self):
859
if self._supports_nesting_repositories is None:
861
self._supports_nesting_repositories = \
862
self._custom_format.supports_nesting_repositories
863
return self._supports_nesting_repositories
780
866
def supports_tree_reference(self):
781
867
if self._supports_tree_reference is None:
782
868
self._ensure_real()
1464
1550
raise errors.UnexpectedSmartServerResponse(response)
1466
1553
def sprout(self, to_bzrdir, revision_id=None):
1467
# TODO: Option to control what format is created?
1469
dest_repo = self._real_repository._format.initialize(to_bzrdir,
1554
"""Create a descendent repository for new development.
1556
Unlike clone, this does not copy the settings of the repository.
1558
dest_repo = self._create_sprouting_repo(to_bzrdir, shared=False)
1471
1559
dest_repo.fetch(self, revision_id=revision_id)
1472
1560
return dest_repo
1562
def _create_sprouting_repo(self, a_bzrdir, shared):
1563
if not isinstance(a_bzrdir._format, self.bzrdir._format.__class__):
1564
# use target default format.
1565
dest_repo = a_bzrdir.create_repository()
1567
# Most control formats need the repository to be specifically
1568
# created, but on some old all-in-one formats it's not needed
1570
dest_repo = self._format.initialize(a_bzrdir, shared=shared)
1571
except errors.UninitializableFormat:
1572
dest_repo = a_bzrdir.open_repository()
1474
1575
### These methods are just thin shims to the VFS object for now.
1476
1578
def revision_tree(self, revision_id):
1478
return self._real_repository.revision_tree(revision_id)
1579
revision_id = _mod_revision.ensure_null(revision_id)
1580
if revision_id == _mod_revision.NULL_REVISION:
1581
return InventoryRevisionTree(self,
1582
Inventory(root_id=None), _mod_revision.NULL_REVISION)
1584
return list(self.revision_trees([revision_id]))[0]
1480
1586
def get_serializer_format(self):
1481
1587
self._ensure_real()
1569
1674
@needs_read_lock
1570
1675
def clone(self, a_bzrdir, revision_id=None):
1572
return self._real_repository.clone(a_bzrdir, revision_id=revision_id)
1676
dest_repo = self._create_sprouting_repo(
1677
a_bzrdir, shared=self.is_shared())
1678
self.copy_content_into(dest_repo, revision_id)
1574
1681
def make_working_trees(self):
1575
1682
"""See Repository.make_working_trees"""
1577
return self._real_repository.make_working_trees()
1683
path = self.bzrdir._path_for_remote_call(self._client)
1685
response = self._call('Repository.make_working_trees', path)
1686
except errors.UnknownSmartMethod:
1688
return self._real_repository.make_working_trees()
1689
if response[0] not in ('yes', 'no'):
1690
raise SmartProtocolError('unexpected response code %s' % (response,))
1691
return response[0] == 'yes'
1579
1693
def refresh_data(self):
1580
1694
"""Re-read any data needed to synchronise with disk.
1682
1796
self._ensure_real()
1683
1797
return self._real_repository.iter_files_bytes(desired_files)
1799
def get_cached_parent_map(self, revision_ids):
1800
"""See bzrlib.CachingParentsProvider.get_cached_parent_map"""
1801
return self._unstacked_provider.get_cached_parent_map(revision_ids)
1685
1803
def get_parent_map(self, revision_ids):
1686
1804
"""See bzrlib.Graph.get_parent_map()."""
1687
1805
return self._make_parents_provider().get_parent_map(revision_ids)
1745
1863
if parents_map is None:
1746
1864
# Repository is not locked, so there's no cache.
1747
1865
parents_map = {}
1748
# start_set is all the keys in the cache
1749
start_set = set(parents_map)
1750
# result set is all the references to keys in the cache
1751
result_parents = set()
1752
for parents in parents_map.itervalues():
1753
result_parents.update(parents)
1754
stop_keys = result_parents.difference(start_set)
1755
# We don't need to send ghosts back to the server as a position to
1757
stop_keys.difference_update(self._unstacked_provider.missing_keys)
1758
key_count = len(parents_map)
1759
if (NULL_REVISION in result_parents
1760
and NULL_REVISION in self._unstacked_provider.missing_keys):
1761
# If we pruned NULL_REVISION from the stop_keys because it's also
1762
# in our cache of "missing" keys we need to increment our key count
1763
# by 1, because the reconsitituted SearchResult on the server will
1764
# still consider NULL_REVISION to be an included key.
1766
included_keys = start_set.intersection(result_parents)
1767
start_set.difference_update(included_keys)
1866
if _DEFAULT_SEARCH_DEPTH <= 0:
1867
(start_set, stop_keys,
1868
key_count) = graph.search_result_from_parent_map(
1869
parents_map, self._unstacked_provider.missing_keys)
1871
(start_set, stop_keys,
1872
key_count) = graph.limited_search_result_from_parent_map(
1873
parents_map, self._unstacked_provider.missing_keys,
1874
keys, depth=_DEFAULT_SEARCH_DEPTH)
1768
1875
recipe = ('manual', start_set, stop_keys, key_count)
1769
1876
body = self._serialise_search_recipe(recipe)
1770
1877
path = self.bzrdir._path_for_remote_call(self._client)
1844
1951
@needs_read_lock
1845
1952
def get_revision_delta(self, revision_id, specific_fileids=None):
1847
return self._real_repository.get_revision_delta(revision_id,
1848
specific_fileids=specific_fileids)
1953
r = self.get_revision(revision_id)
1954
return list(self.get_deltas_for_revisions([r],
1955
specific_fileids=specific_fileids))[0]
1850
1957
@needs_read_lock
1851
1958
def revision_trees(self, revision_ids):
1864
1971
callback_refs=callback_refs, check_repo=check_repo)
1866
1973
def copy_content_into(self, destination, revision_id=None):
1868
return self._real_repository.copy_content_into(
1869
destination, revision_id=revision_id)
1974
"""Make a complete copy of the content in self into destination.
1976
This is a destructive operation! Do not use it on existing
1979
interrepo = _mod_repository.InterRepository.get(self, destination)
1980
return interrepo.copy_content(revision_id)
1871
1982
def _copy_repository_tarball(self, to_bzrdir, revision_id=None):
1872
1983
# get a tarball of the remote repository, and copy from that into the
1874
1985
from bzrlib import osutils
1876
1987
# TODO: Maybe a progress bar while streaming the tarball?
1877
note("Copying repository content as tarball...")
1988
note(gettext("Copying repository content as tarball..."))
1878
1989
tar_file = self._get_tarball('bz2')
1879
1990
if tar_file is None:
1985
2096
def _serializer(self):
1986
2097
return self._format._serializer
1988
2100
def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
1990
return self._real_repository.store_revision_signature(
1991
gpg_strategy, plaintext, revision_id)
2101
signature = gpg_strategy.sign(plaintext)
2102
self.add_signature_text(revision_id, signature)
1993
2104
def add_signature_text(self, revision_id, signature):
1994
2105
self._ensure_real()
1995
2106
return self._real_repository.add_signature_text(revision_id, signature)
1997
2108
def has_signature_for_revision_id(self, revision_id):
2109
path = self.bzrdir._path_for_remote_call(self._client)
2111
response = self._call('Repository.has_signature_for_revision_id',
2113
except errors.UnknownSmartMethod:
2115
return self._real_repository.has_signature_for_revision_id(
2117
if response[0] not in ('yes', 'no'):
2118
raise SmartProtocolError('unexpected response code %s' % (response,))
2119
return (response[0] == 'yes')
2121
def verify_revision_signature(self, revision_id, gpg_strategy):
1998
2122
self._ensure_real()
1999
return self._real_repository.has_signature_for_revision_id(revision_id)
2123
return self._real_repository.verify_revision_signature(
2124
revision_id, gpg_strategy)
2001
2126
def item_keys_introduced_by(self, revision_ids, _files_pb=None):
2002
2127
self._ensure_real()
2354
2479
return a_bzrdir.open_branch(name=name,
2355
2480
ignore_fallbacks=ignore_fallbacks)
2357
def _vfs_initialize(self, a_bzrdir, name):
2482
def _vfs_initialize(self, a_bzrdir, name, append_revisions_only):
2358
2483
# Initialisation when using a local bzrdir object, or a non-vfs init
2359
2484
# method is not available on the server.
2360
2485
# self._custom_format is always set - the start of initialize ensures
2362
2487
if isinstance(a_bzrdir, RemoteBzrDir):
2363
2488
a_bzrdir._ensure_real()
2364
2489
result = self._custom_format.initialize(a_bzrdir._real_bzrdir,
2490
name, append_revisions_only=append_revisions_only)
2367
2492
# We assume the bzrdir is parameterised; it may not be.
2368
result = self._custom_format.initialize(a_bzrdir, name)
2493
result = self._custom_format.initialize(a_bzrdir, name,
2494
append_revisions_only=append_revisions_only)
2369
2495
if (isinstance(a_bzrdir, RemoteBzrDir) and
2370
2496
not isinstance(result, RemoteBranch)):
2371
2497
result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result,
2375
def initialize(self, a_bzrdir, name=None, repository=None):
2501
def initialize(self, a_bzrdir, name=None, repository=None,
2502
append_revisions_only=None):
2376
2503
# 1) get the network name to use.
2377
2504
if self._custom_format:
2378
2505
network_name = self._custom_format.network_name()
2384
2511
network_name = reference_format.network_name()
2385
2512
# Being asked to create on a non RemoteBzrDir:
2386
2513
if not isinstance(a_bzrdir, RemoteBzrDir):
2387
return self._vfs_initialize(a_bzrdir, name=name)
2514
return self._vfs_initialize(a_bzrdir, name=name,
2515
append_revisions_only=append_revisions_only)
2388
2516
medium = a_bzrdir._client._medium
2389
2517
if medium._is_remote_before((1, 13)):
2390
return self._vfs_initialize(a_bzrdir, name=name)
2518
return self._vfs_initialize(a_bzrdir, name=name,
2519
append_revisions_only=append_revisions_only)
2391
2520
# Creating on a remote bzr dir.
2392
2521
# 2) try direct creation via RPC
2393
2522
path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
2400
2529
except errors.UnknownSmartMethod:
2401
2530
# Fallback - use vfs methods
2402
2531
medium._remember_remote_is_before((1, 13))
2403
return self._vfs_initialize(a_bzrdir, name=name)
2532
return self._vfs_initialize(a_bzrdir, name=name,
2533
append_revisions_only=append_revisions_only)
2404
2534
if response[0] != 'ok':
2405
2535
raise errors.UnexpectedSmartServerResponse(response)
2406
2536
# Turn the response into a RemoteRepository object.
2427
2557
remote_repo = RemoteRepository(repo_bzrdir, repo_format)
2428
2558
remote_branch = RemoteBranch(a_bzrdir, remote_repo,
2429
2559
format=format, setup_stacking=False, name=name)
2560
if append_revisions_only:
2561
remote_branch.set_append_revisions_only(append_revisions_only)
2430
2562
# XXX: We know this is a new branch, so it must have revno 0, revid
2431
2563
# NULL_REVISION. Creating the branch locked would make this be unable
2432
2564
# to be wrong; here its simply very unlikely to be wrong. RBC 20090225
2601
class RemoteBranchStore(config.IniFileStore):
2602
"""Branch store which attempts to use HPSS calls to retrieve branch store.
2604
Note that this is specific to bzr-based formats.
2607
def __init__(self, branch):
2608
super(RemoteBranchStore, self).__init__()
2609
self.branch = branch
2611
self._real_store = None
2613
def lock_write(self, token=None):
2614
return self.branch.lock_write(token)
2617
return self.branch.unlock()
2621
# We need to be able to override the undecorated implementation
2622
self.save_without_locking()
2624
def save_without_locking(self):
2625
super(RemoteBranchStore, self).save()
2627
def external_url(self):
2628
return self.branch.user_url
2630
def _load_content(self):
2631
path = self.branch._remote_path()
2633
response, handler = self.branch._call_expecting_body(
2634
'Branch.get_config_file', path)
2635
except errors.UnknownSmartMethod:
2637
return self._real_store._load_content()
2638
if len(response) and response[0] != 'ok':
2639
raise errors.UnexpectedSmartServerResponse(response)
2640
return handler.read_body_bytes()
2642
def _save_content(self, content):
2643
path = self.branch._remote_path()
2645
response, handler = self.branch._call_with_body_bytes_expecting_body(
2646
'Branch.put_config_file', (path,
2647
self.branch._lock_token, self.branch._repo_lock_token),
2649
except errors.UnknownSmartMethod:
2651
return self._real_store._save_content(content)
2652
handler.cancel_read_body()
2653
if response != ('ok', ):
2654
raise errors.UnexpectedSmartServerResponse(response)
2656
def _ensure_real(self):
2657
self.branch._ensure_real()
2658
if self._real_store is None:
2659
self._real_store = config.BranchStore(self.branch)
2468
2662
class RemoteBranch(branch.Branch, _RpcHelper, lock._RelockDebugMixin):
2469
2663
"""Branch stored on a server accessed by HPSS RPC.
2559
2753
def _get_config(self):
2560
2754
return RemoteBranchConfig(self)
2756
def _get_config_store(self):
2757
return RemoteBranchStore(self)
2562
2759
def _get_real_transport(self):
2563
2760
# if we try vfs access, return the real branch's vfs transport
2564
2761
self._ensure_real()
2627
2824
self.bzrdir, self._client)
2628
2825
return self._control_files
2630
def _get_checkout_format(self):
2827
def _get_checkout_format(self, lightweight=False):
2631
2828
self._ensure_real()
2632
return self._real_branch._get_checkout_format()
2830
format = RemoteBzrDirFormat()
2831
self.bzrdir._format._supply_sub_formats_to(format)
2832
format.workingtree_format = self._real_branch._get_checkout_format(
2833
lightweight=lightweight).workingtree_format
2836
return self._real_branch._get_checkout_format(lightweight=False)
2634
2838
def get_physical_lock_status(self):
2635
2839
"""See Branch.get_physical_lock_status()."""