1 # Copyright (C) 2006-2007 Jelmer Vernooij <jelmer@samba.org>
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
16 """Subversion BzrDir formats."""
18 from bzrlib import urlutils
19 from bzrlib.bzrdir import BzrDirFormat, BzrDir, format_registry
20 from bzrlib.errors import (NotBranchError, NotLocalUrl, NoRepositoryPresent,
21 NoWorkingTree, AlreadyBranchError)
22 from bzrlib.lockable_files import TransportLock
23 from bzrlib.transport.local import LocalTransport
25 from svn.core import SubversionException
26 import svn.core, svn.repos
28 from errors import NoSvnRepositoryPresent
29 from repository import SvnRepository
30 from transport import SvnRaTransport, bzr_to_svn_url, get_svn_ra_transport
32 def get_rich_root_format():
33 format = BzrDirFormat.get_default_format()
34 if format.repository_format.rich_root_data:
36 # Default format does not support rich root data,
37 # fall back to dirstate-with-subtree
38 format = format_registry.make_bzrdir('dirstate-with-subtree')
39 assert format.repository_format.rich_root_data
43 class SvnRemoteAccess(BzrDir):
44 """BzrDir implementation for Subversion connections.
46 This is used for all non-checkout connections
47 to Subversion repositories.
49 def __init__(self, _transport, _format):
50 """See BzrDir.__init__()."""
51 _transport = get_svn_ra_transport(_transport)
52 self._format = _format
54 self.root_transport = _transport
56 svn_url = bzr_to_svn_url(self.root_transport.base)
57 self.svn_root_url = _transport.get_repos_root()
59 assert svn_url.startswith(self.svn_root_url)
60 self.branch_path = svn_url[len(self.svn_root_url):]
62 def clone(self, url, revision_id=None, force_new_repo=False):
63 """See BzrDir.clone().
65 Not supported on Subversion connections.
67 raise NotImplementedError(SvnRemoteAccess.clone)
69 def sprout(self, url, revision_id=None, force_new_repo=False,
70 recurse='down', possible_transports=None):
71 """See BzrDir.sprout()."""
72 # FIXME: Use possible_transports
74 format = get_rich_root_format()
75 result = format.initialize(url)
76 repo = self.find_repository()
78 result_repo = repo.clone(result, revision_id)
81 result_repo = result.find_repository()
82 result_repo.fetch(repo, revision_id=revision_id)
83 except NoRepositoryPresent:
84 result_repo = repo.clone(result, revision_id)
85 branch = self.open_branch()
86 result_branch = branch.sprout(result, revision_id)
87 if result_branch.repository.make_working_trees():
88 result.create_workingtree()
91 def open_repository(self, _unsupported=False):
92 """Open the repository associated with this BzrDir.
94 :return: instance of SvnRepository.
96 if self.branch_path == "":
97 return SvnRepository(self, self.root_transport)
98 raise NoSvnRepositoryPresent(self.root_transport.base)
100 def find_repository(self):
101 """Open the repository associated with this BzrDir.
103 :return: instance of SvnRepository.
105 transport = self.root_transport
106 if self.svn_root_url != transport.base:
107 transport = transport.clone_root()
108 return SvnRepository(self, transport, self.branch_path)
110 def open_workingtree(self, _unsupported=False,
111 recommend_upgrade=True):
112 """See BzrDir.open_workingtree().
114 Will always raise NotLocalUrl as this
115 BzrDir can not be associated with working trees.
117 # Working trees never exist on remote Subversion repositories
118 raise NoWorkingTree(self.root_transport.base)
120 def create_workingtree(self, revision_id=None):
121 """See BzrDir.create_workingtree().
123 Will always raise NotLocalUrl as this
124 BzrDir can not be associated with working trees.
126 raise NotLocalUrl(self.root_transport.base)
128 def needs_format_conversion(self, format=None):
129 """See BzrDir.needs_format_conversion()."""
130 # if the format is not the same as the system default,
131 # an upgrade is needed.
133 format = BzrDirFormat.get_default_format()
134 return not isinstance(self._format, format.__class__)
136 def import_branch(self, source, stop_revision=None):
137 """Create a new branch in this repository, possibly
138 with the specified history, optionally importing revisions.
140 :param source: Source branch
141 :param stop_revision: Tip of new branch
142 :return: Branch object
144 from commit import push_new
145 if stop_revision is None:
146 stop_revision = source.last_revision()
147 target_branch_path = self.branch_path.strip("/")
148 repos = self.find_repository()
149 full_branch_url = urlutils.join(repos.transport.base,
151 if repos.transport.check_path(target_branch_path,
152 repos.transport.get_latest_revnum()) != svn.core.svn_node_none:
153 raise AlreadyBranchError(full_branch_url)
154 push_new(repos, target_branch_path, source, stop_revision)
155 branch = self.open_branch()
156 branch.pull(source, stop_revision=stop_revision)
159 def create_branch(self):
160 """See BzrDir.create_branch()."""
161 from branch import SvnBranch
162 repos = self.find_repository()
164 if self.branch_path != "":
165 # TODO: Set NULL_REVISION in SVN_PROP_BZR_BRANCHING_SCHEME
166 repos.transport.mkdir(self.branch_path.strip("/"))
167 elif repos.transport.get_latest_revnum() > 0:
168 # Bail out if there are already revisions in this repository
169 raise AlreadyBranchError(self.root_transport.base)
170 branch = SvnBranch(self.root_transport.base, repos, self.branch_path)
174 def open_branch(self, unsupported=True):
175 """See BzrDir.open_branch()."""
176 from branch import SvnBranch
177 repos = self.find_repository()
178 branch = SvnBranch(self.root_transport.base, repos, self.branch_path)
182 def create_repository(self, shared=False, format=None):
183 """See BzrDir.create_repository."""
184 return self.open_repository()
187 class SvnFormat(BzrDirFormat):
188 """Format for the Subversion smart server."""
189 _lock_class = TransportLock
192 super(SvnFormat, self).__init__()
193 from repository import SvnRepositoryFormat
194 self.repository_format = SvnRepositoryFormat()
197 def probe_transport(klass, transport):
201 transport = get_svn_ra_transport(transport)
202 except SubversionException, (_, num):
203 if num in (svn.core.SVN_ERR_RA_ILLEGAL_URL, \
204 svn.core.SVN_ERR_RA_LOCAL_REPOS_OPEN_FAILED, \
205 svn.core.SVN_ERR_BAD_URL):
206 raise NotBranchError(path=transport.base)
208 if isinstance(transport, SvnRaTransport):
211 raise NotBranchError(path=transport.base)
213 def _open(self, transport):
215 return SvnRemoteAccess(transport, self)
216 except SubversionException, (_, num):
217 if num == svn.core.SVN_ERR_RA_DAV_REQUEST_FAILED:
218 raise NotBranchError(transport.base)
221 def get_format_string(self):
222 return 'Subversion Smart Server'
224 def get_format_description(self):
225 return 'Subversion Smart Server'
227 def initialize_on_transport(self, transport):
228 """See BzrDir.initialize_on_transport()."""
229 if not isinstance(transport, LocalTransport):
230 raise NotImplementedError(self.initialize,
231 "Can't create Subversion Repositories/branches on "
232 "non-local transports")
234 local_path = transport._local_base.rstrip("/")
235 svn.repos.create(local_path, '', '', None, None)
236 return self.open(get_svn_ra_transport(transport), _found=True)
238 def is_supported(self):
239 """See BzrDir.is_supported()."""