1
# Copyright (C) 2006 Canonical Ltd
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
# TODO: At some point, handle upgrades by just passing the whole request
18
# across to run on the server.
20
from urlparse import urlparse
22
from bzrlib import branch, errors, repository
23
from bzrlib.bzrdir import BzrDir, BzrDirFormat, RemoteBzrDirFormat
24
from bzrlib.branch import Branch, BranchFormat, BranchReferenceFormat
25
from bzrlib.smart import client, vfs
26
from bzrlib.trace import mutter
27
from bzrlib.urlutils import unescape
29
# Note: RemoteBzrDirFormat is in bzrdir.py
31
class RemoteBzrDir(BzrDir):
32
"""Control directory on a remote server, accessed by HPSS."""
34
def __init__(self, transport):
35
BzrDir.__init__(self, transport, RemoteBzrDirFormat())
36
self.client = transport.get_smart_client()
37
# this object holds a delegated bzrdir that uses file-level operations
38
# to talk to the other side
39
# XXX: We should go into find_format, but not allow it to find
40
# RemoteBzrDirFormat and make sure it finds the real underlying format.
42
# THIS IS A COMPLETE AND UTTER LIE.
43
# XXX: XXX: XXX: must be removed before merging to mainline
44
# SMART_SERVER_MERGE_BLOCKER
45
default_format = BzrDirFormat.get_default_format()
46
self._real_bzrdir = default_format.open(transport, _found=True)
47
path = self._path_for_remote_call()
48
#self._real_bzrdir._format.probe_transport(transport)
49
response = client.SmartClient(self.client).call('probe_dont_use', path)
50
if response == ('no',):
51
raise errors.NotBranchError(path=transport.base)
54
def create_repository(self, shared=False):
55
return RemoteRepository(
56
self, self._real_bzrdir.create_repository(shared=shared))
58
def create_branch(self):
59
real_branch = self._real_bzrdir.create_branch()
60
real_repository = real_branch.repository
61
remote_repository = RemoteRepository(self, real_repository)
62
return RemoteBranch(self, remote_repository, real_branch)
64
def create_workingtree(self, revision_id=None):
65
real_workingtree = self._real_bzrdir.create_workingtree(revision_id=revision_id)
66
return RemoteWorkingTree(self, real_workingtree)
68
def open_branch(self, _unsupported=False):
69
assert _unsupported == False, 'unsupported flag support not implemented yet.'
70
path = self._path_for_remote_call()
71
response = client.SmartClient(self.client).call('BzrDir.open_branch', path)
72
assert response[0] == 'ok', 'unexpected response code %s' % response[0]
73
if response[0] != 'ok':
74
# this should probably be a regular translate no ?
75
raise errors.NotBranchError(path=self.root_transport.base)
77
# branch at this location.
79
# if the VFS is enabled, create a local object using the VFS.
80
real_branch = self._real_bzrdir.open_branch(unsupported=_unsupported)
81
# This branch accessed through the smart server, so wrap the
83
real_repository = real_branch.repository
84
remote_repository = RemoteRepository(self, real_repository)
85
return RemoteBranch(self, remote_repository, real_branch)
87
# otherwise just create a proxy for the branch.
88
return RemoteBranch(self, self.find_repository())
90
# a branch reference, use the existing BranchReference logic.
91
format = BranchReferenceFormat()
92
return format.open(self, _found=True, location=response[1])
94
def open_repository(self):
95
path = self._path_for_remote_call()
96
response = client.SmartClient(self.client).call('BzrDir.find_repository', path)
97
assert response[0] == 'ok', 'unexpected response code %s' % response[0]
100
return RemoteRepository(self, self._real_bzrdir.open_repository())
102
return RemoteRepository(self)
104
raise errors.NoRepositoryPresent(self)
106
def open_workingtree(self):
107
return RemoteWorkingTree(self, self._real_bzrdir.open_workingtree())
109
def _path_for_remote_call(self):
110
"""Return the path to be used for this bzrdir in a remote call."""
111
return unescape(urlparse(self.root_transport.base)[2])
113
def get_branch_transport(self, branch_format):
114
return self._real_bzrdir.get_branch_transport(branch_format)
116
def get_repository_transport(self, repository_format):
117
return self._real_bzrdir.get_repository_transport(repository_format)
119
def get_workingtree_transport(self, workingtree_format):
120
return self._real_bzrdir.get_workingtree_transport(workingtree_format)
122
def can_convert_format(self):
123
"""Upgrading of remote bzrdirs is not supported yet."""
126
def needs_format_conversion(self, format=None):
127
"""Upgrading of remote bzrdirs is not supported yet."""
131
class RemoteRepositoryFormat(repository.RepositoryFormat):
132
"""Format for repositories accessed over rpc.
134
Instances of this repository are represented by RemoteRepository
138
_matchingbzrdir = RemoteBzrDirFormat
140
def initialize(self, a_bzrdir, shared=False):
141
assert isinstance(a_bzrdir, RemoteBzrDir)
142
return a_bzrdir.create_repository(shared=shared)
144
def open(self, a_bzrdir):
145
assert isinstance(a_bzrdir, RemoteBzrDir)
146
return a_bzrdir.open_repository()
148
def get_format_description(self):
149
return 'bzr remote repository'
151
def __eq__(self, other):
152
return self.__class__ == other.__class__
154
rich_root_data = False
157
class RemoteRepository(object):
158
"""Repository accessed over rpc.
160
For the moment everything is delegated to IO-like operations over
164
def __init__(self, remote_bzrdir, real_repository=None):
165
"""Create a RemoteRepository instance.
167
:param remote_bzrdir: The bzrdir hosting this repository.
168
:param real_repository: If not None, a local implementation of the
169
repository logic for the repository, usually accessing the data
173
self._real_repository = _real_repository
174
self.bzrdir = remote_bzrdir
175
self._format = RemoteRepositoryFormat()
178
class RemoteBranchFormat(branch.BranchFormat):
180
def open(self, a_bzrdir):
181
assert isinstance(a_bzrdir, RemoteBzrDir)
182
return a_bzrdir.open_branch()
184
def initialize(self, a_bzrdir):
185
assert isinstance(a_bzrdir, RemoteBzrDir)
186
return a_bzrdir.create_branch()
189
class RemoteBranch(branch.Branch):
190
"""Branch stored on a server accessed by HPSS RPC.
192
At the moment most operations are mapped down to simple file operations.
195
def __init__(self, remote_bzrdir, remote_repository, real_branch=None):
196
"""Create a RemoteBranch instance.
198
:param real_branch: An optional local implementation of the branch
199
format, usually accessing the data via the VFS.
201
self.bzrdir = remote_bzrdir
202
self.repository = remote_repository
203
if real_branch is not None:
204
self._real_branch = real_branch
205
self._format = RemoteBranchFormat()
208
return self._real_branch.lock_read()
210
def lock_write(self):
211
return self._real_branch.lock_write()
214
return self._real_branch.unlock()
216
def break_lock(self):
217
return self._real_branch.break_lock()
219
def revision_history(self):
220
return self._real_branch.revision_history()
222
def set_revision_history(self, rev_history):
223
return self._real_branch.set_revision_history(rev_history)
225
def get_parent(self):
226
return self._real_branch.get_parent()
228
def set_parent(self, url):
229
return self._real_branch.set_parent(url)
232
class RemoteWorkingTree(object):
234
def __init__(self, remote_bzrdir, real_workingtree):
235
self.real_workingtree = real_workingtree
236
self.bzrdir = remote_bzrdir
238
def __getattr__(self, name):
239
# XXX: temporary way to lazily delegate everything to the real
241
return getattr(self.real_workingtree, name)