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
return RemoteRepository(self, self._real_bzrdir.open_repository())
97
def open_workingtree(self):
98
return RemoteWorkingTree(self, self._real_bzrdir.open_workingtree())
100
def _path_for_remote_call(self):
101
"""Return the path to be used for this bzrdir in a remote call."""
102
return unescape(urlparse(self.root_transport.base)[2])
104
def get_branch_transport(self, branch_format):
105
return self._real_bzrdir.get_branch_transport(branch_format)
107
def get_repository_transport(self, repository_format):
108
return self._real_bzrdir.get_repository_transport(repository_format)
110
def get_workingtree_transport(self, workingtree_format):
111
return self._real_bzrdir.get_workingtree_transport(workingtree_format)
113
def can_convert_format(self):
114
"""Upgrading of remote bzrdirs is not supported yet."""
117
def needs_format_conversion(self, format=None):
118
"""Upgrading of remote bzrdirs is not supported yet."""
122
class RemoteRepositoryFormat(repository.RepositoryFormat):
123
"""Format for repositories accessed over rpc.
125
Instances of this repository are represented by RemoteRepository
129
_matchingbzrdir = RemoteBzrDirFormat
131
def initialize(self, a_bzrdir, shared=False):
132
assert isinstance(a_bzrdir, RemoteBzrDir)
133
return a_bzrdir.create_repository(shared=shared)
135
def open(self, a_bzrdir):
136
assert isinstance(a_bzrdir, RemoteBzrDir)
137
return a_bzrdir.open_repository()
139
def get_format_description(self):
140
return 'bzr remote repository'
142
def __eq__(self, other):
143
return self.__class__ == other.__class__
145
rich_root_data = False
148
class RemoteRepository(object):
149
"""Repository accessed over rpc.
151
For the moment everything is delegated to IO-like operations over
155
def __init__(self, remote_bzrdir, real_repository):
156
self.real_repository = real_repository
157
self.bzrdir = remote_bzrdir
158
self._format = RemoteRepositoryFormat()
160
def __getattr__(self, name):
161
# XXX: temporary way to lazily delegate everything to the real
163
return getattr(self.real_repository, name)
166
class RemoteBranchFormat(branch.BranchFormat):
168
def open(self, a_bzrdir):
169
assert isinstance(a_bzrdir, RemoteBzrDir)
170
return a_bzrdir.open_branch()
172
def initialize(self, a_bzrdir):
173
assert isinstance(a_bzrdir, RemoteBzrDir)
174
return a_bzrdir.create_branch()
177
class RemoteBranch(branch.Branch):
178
"""Branch stored on a server accessed by HPSS RPC.
180
At the moment most operations are mapped down to simple file operations.
183
def __init__(self, remote_bzrdir, remote_repository, real_branch):
184
self.bzrdir = remote_bzrdir
185
self.transport = remote_bzrdir.transport
186
self.repository = remote_repository
187
self._real_branch = real_branch
188
self._format = RemoteBranchFormat()
191
return self._real_branch.lock_read()
193
def lock_write(self):
194
return self._real_branch.lock_write()
197
return self._real_branch.unlock()
199
def break_lock(self):
200
return self._real_branch.break_lock()
202
def revision_history(self):
203
return self._real_branch.revision_history()
205
def set_revision_history(self, rev_history):
206
return self._real_branch.set_revision_history(rev_history)
208
def get_parent(self):
209
return self._real_branch.get_parent()
211
def set_parent(self, url):
212
return self._real_branch.set_parent(url)
215
class RemoteWorkingTree(object):
217
def __init__(self, remote_bzrdir, real_workingtree):
218
self.real_workingtree = real_workingtree
219
self.bzrdir = remote_bzrdir
221
def __getattr__(self, name):
222
# XXX: temporary way to lazily delegate everything to the real
224
return getattr(self.real_workingtree, name)