1 # Copyright (C) 2005-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 3 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
18 Support for Subversion branches
21 from bzrlib.bzrdir import BzrDirFormat, format_registry
22 from bzrlib.commands import Command, register_command, display_command, Option
23 from bzrlib.help_topics import topic_registry
24 from bzrlib.revisionspec import SPEC_TYPES
25 from bzrlib.trace import warning, mutter
26 from bzrlib.transport import register_lazy_transport, register_transport_proto
28 from bzrlib.plugins.svn import format
29 from bzrlib.plugins.svn import revspec
31 # versions ending in 'exp' mean experimental mappings
32 # versions ending in 'dev' mean development version
33 # versions ending in 'final' mean release (well tested, etc)
34 version_info = (0, 4, 11, 'dev', 0)
36 if version_info[3] == 'final':
37 version_string = '%d.%d.%d' % version_info[:3]
39 version_string = '%d.%d.%d%s%d' % version_info
40 __version__ = version_string
42 COMPATIBLE_BZR_VERSIONS = [(1, 4), (1, 5), (1, 6)]
44 def check_bzrlib_version(desired):
45 """Check that bzrlib is compatible.
47 If version is < all compatible version, assume incompatible.
48 If version is compatible version + 1, assume compatible, with deprecations
49 Otherwise, assume incompatible.
52 bzrlib_version = bzrlib.version_info[:2]
53 if (bzrlib_version in desired or
54 ((bzrlib_version[0], bzrlib_version[1]-1) in desired and
55 bzrlib.version_info[3] == 'dev')):
57 from bzrlib.errors import BzrError
58 if bzrlib_version < desired[0]:
59 raise BzrError('Installed bzr version %s is too old to be used with bzr-svn, at least %s.%s required' % (bzrlib.__version__, desired[0][0], desired[0][1]))
61 warning('bzr-svn is not up to date with installed bzr version %s.'
62 ' \nThere should be a newer version of bzr-svn available.',
64 if not (bzrlib_version[0], bzrlib_version[1]-1) in desired:
65 raise BzrError('Version mismatch')
67 def check_subversion_version():
68 """Check that Subversion is compatible.
72 from bzrlib.plugins.svn import core
74 warning("Unable to load bzr-svn extensions - did you build it?")
75 from bzrlib.plugins.svn.ra import version
76 ra_version = version()
77 if (ra_version[0] >= 5 and getattr(ra, 'SVN_REVISION', None) and 27729 <= ra.SVN_REVISION < 31470):
78 warning('Installed Subversion has buggy svn.ra.get_log() implementation, please install newer.')
80 mutter("bzr-svn: using Subversion %d.%d.%d (%s)" % ra_version)
83 def check_rebase_version(min_version):
84 """Check what version of bzr-rebase is installed.
86 Raises an exception when the version installed is older than
89 :raises RebaseNotPresent: Raised if bzr-rebase is not installed or too old.
91 from bzrlib.plugins.svn.errors import RebaseNotPresent
93 from bzrlib.plugins.rebase import version_info as rebase_version_info
94 if rebase_version_info[:2] < min_version:
95 raise RebaseNotPresent("Version %r present, at least %r required"
96 % (rebase_version_info, min_version))
97 except ImportError, e:
98 raise RebaseNotPresent(e)
101 check_subversion_version()
103 register_transport_proto('svn+ssh://',
104 help="Access using the Subversion smart server tunneled over SSH.")
105 register_transport_proto('svn+file://',
106 help="Access of local Subversion repositories.")
107 register_transport_proto('svn+http://',
108 help="Access of Subversion smart servers over HTTP.")
109 register_transport_proto('svn+https://',
110 help="Access of Subversion smart servers over secure HTTP.")
111 register_transport_proto('svn://',
112 help="Access using the Subversion smart server.")
113 register_lazy_transport('svn://', 'bzrlib.plugins.svn.transport',
115 register_lazy_transport('svn+', 'bzrlib.plugins.svn.transport',
117 topic_registry.register_lazy('svn-branching-schemes',
118 'bzrlib.plugins.svn.mapping3.scheme',
119 'help_schemes', 'Subversion branching schemes')
121 BzrDirFormat.register_control_format(format.SvnRemoteFormat)
122 BzrDirFormat.register_control_format(format.SvnWorkingTreeDirFormat)
123 format_registry.register("subversion", format.SvnRemoteFormat,
124 "Subversion repository. ",
126 format_registry.register("subversion-wc", format.SvnWorkingTreeDirFormat,
127 "Subversion working copy. ",
128 native=False, hidden=True)
129 SPEC_TYPES.append(revspec.RevisionSpec_svn)
131 versions_checked = False
132 def lazy_check_versions():
133 """Check whether all dependencies have the right versions.
135 :note: Only checks once, caches the result."""
136 global versions_checked
139 versions_checked = True
140 check_bzrlib_version(COMPATIBLE_BZR_VERSIONS)
142 optimizers_registered = False
143 def lazy_register_optimizers():
144 """Register optimizers for fetching between Subversion and Bazaar
147 :note: Only registers on the first call."""
148 global optimizers_registered
149 if optimizers_registered:
151 from bzrlib.repository import InterRepository
152 from bzrlib.plugins.svn import commit, fetch
153 optimizers_registered = True
154 InterRepository.register_optimiser(fetch.InterFromSvnRepository)
155 InterRepository.register_optimiser(commit.InterToSvnRepository)
158 def get_scheme(schemename):
159 """Parse scheme identifier and return a branching scheme.
161 :param schemename: Name of the scheme to retrieve.
163 if isinstance(schemename, unicode):
164 schemename = schemename.encode("ascii")
165 from bzrlib.plugins.svn.mapping3.scheme import BranchingScheme
166 from bzrlib.errors import BzrCommandError
168 ret = BranchingScheme.find_scheme(schemename)
170 raise BzrCommandError('No such branching scheme %r' % schemename)
174 class cmd_svn_import(Command):
175 """Convert a Subversion repository to a Bazaar repository.
178 takes_args = ['from_location', 'to_location?']
179 takes_options = [Option('trees', help='Create working trees.'),
180 Option('standalone', help='Create standalone branches.'),
182 help='Convert all revisions, even those not in '
183 'current branch history (forbids --standalone).'),
184 Option('scheme', type=get_scheme,
185 help='Branching scheme (none, trunk, etc). '
187 Option('prefix', type=str,
188 help='Only consider branches of which path starts '
193 def run(self, from_location, to_location=None, trees=False,
194 standalone=False, scheme=None, all=False, prefix=None):
195 from bzrlib.branch import Branch
196 from bzrlib.bzrdir import BzrDir
197 from bzrlib.errors import BzrCommandError, NoRepositoryPresent, NotBranchError
198 from bzrlib import urlutils
199 from bzrlib.plugins.svn.convert import convert_repository
200 from bzrlib.plugins.svn.repository import SvnRepository
203 if to_location is None:
204 to_location = os.path.basename(from_location.rstrip("/\\"))
207 # All implies shared repository
208 # (otherwise there is no repository to store revisions in)
211 if os.path.isfile(from_location):
212 from bzrlib.plugins.svn.convert import load_dumpfile
214 tmp_repos = tempfile.mkdtemp(prefix='bzr-svn-dump-')
215 load_dumpfile(from_location, tmp_repos)
216 from_location = tmp_repos
220 from_dir = BzrDir.open(from_location)
222 from_repos = from_dir.open_repository()
223 except NoRepositoryPresent, e:
225 Branch.open(from_location)
226 raise BzrCommandError("No Repository found at %s. "
227 "For individual branches, use 'bzr branch'." % from_location)
228 except NotBranchError:
229 if prefix is not None:
230 raise BzrCommandError("Path inside repository specified and --prefix specified")
231 from_repos = from_dir.find_repository()
232 prefix = urlutils.relative_url(from_repos.base, from_location)
233 self.outf.write("Importing branches below %s\n" %
234 urlutils.unescape_for_display(prefix, self.outf.encoding))
236 if prefix is not None:
237 prefix = prefix.strip("/") + "/"
239 if not isinstance(from_repos, SvnRepository):
240 raise BzrCommandError(
241 "Not a Subversion repository: %s" % from_location)
243 def filter_branch(branch):
244 if prefix is not None and not branch.get_branch_path().startswith(prefix):
248 convert_repository(from_repos, to_location, scheme, None,
249 not standalone, trees, all, filter_branch=filter_branch)
251 if tmp_repos is not None:
252 from bzrlib import osutils
253 osutils.rmtree(tmp_repos)
256 register_command(cmd_svn_import)
258 class cmd_svn_upgrade(Command):
259 """Upgrade revisions mapped from Subversion in a Bazaar branch.
261 This will change the revision ids of revisions whose parents
262 were mapped from svn revisions.
264 takes_args = ['from_repository?']
265 takes_options = ['verbose']
268 def run(self, from_repository=None, verbose=False):
269 from bzrlib.plugins.svn.upgrade import upgrade_branch, upgrade_workingtree
270 from bzrlib.branch import Branch
271 from bzrlib.errors import NoWorkingTree, BzrCommandError
272 from bzrlib.repository import Repository
273 from bzrlib.trace import info
274 from bzrlib.workingtree import WorkingTree
276 wt_to = WorkingTree.open(".")
277 branch_to = wt_to.branch
278 except NoWorkingTree:
280 branch_to = Branch.open(".")
282 stored_loc = branch_to.get_parent()
283 if from_repository is None:
284 if stored_loc is None:
285 raise BzrCommandError("No pull location known or"
288 import bzrlib.urlutils as urlutils
289 display_url = urlutils.unescape_for_display(stored_loc,
291 self.outf.write("Using saved location: %s\n" % display_url)
292 from_repository = Branch.open(stored_loc).repository
294 from_repository = Repository.open(from_repository)
296 if wt_to is not None:
297 renames = upgrade_workingtree(wt_to, from_repository,
298 allow_changes=True, verbose=verbose)
300 renames = upgrade_branch(branch_to, from_repository,
301 allow_changes=True, verbose=verbose)
304 info("Nothing to do.")
306 if wt_to is not None:
307 wt_to.set_last_revision(branch_to.last_revision())
309 register_command(cmd_svn_upgrade)
311 class cmd_svn_push(Command):
312 """Push revisions to Subversion, creating a new branch if necessary.
314 The behaviour of this command is the same as that of "bzr push", except
315 that it also creates new branches.
317 This command is experimental and will be removed in the future when all
318 functionality is included in "bzr push".
320 takes_args = ['location?']
321 takes_options = ['revision', 'remember', Option('directory',
322 help='Branch to push from, '
323 'rather than the one containing the working directory.',
328 def run(self, location=None, revision=None, remember=False,
330 from bzrlib.bzrdir import BzrDir
331 from bzrlib.branch import Branch
332 from bzrlib.errors import NotBranchError, BzrCommandError
333 from bzrlib import urlutils
335 if directory is None:
337 source_branch = Branch.open_containing(directory)[0]
338 stored_loc = source_branch.get_push_location()
340 if stored_loc is None:
341 raise BzrCommandError("No push location known or specified.")
343 display_url = urlutils.unescape_for_display(stored_loc,
345 self.outf.write("Using saved location: %s\n" % display_url)
346 location = stored_loc
348 bzrdir = BzrDir.open(location)
349 if revision is not None:
350 if len(revision) > 1:
351 raise BzrCommandError(
352 'bzr svn-push --revision takes exactly one revision'
354 revision_id = revision[0].as_revision_id(source_branch)
358 target_branch = bzrdir.open_branch()
359 target_branch.lock_write()
361 target_branch.pull(source_branch, revision_id)
363 target_branch.unlock()
364 except NotBranchError:
365 target_branch = bzrdir.import_branch(source_branch, revision_id)
366 # We successfully created the target, remember it
367 if source_branch.get_push_location() is None or remember:
368 source_branch.set_push_location(target_branch.base)
370 register_command(cmd_svn_push)
373 class cmd_svn_branching_scheme(Command):
374 """Show or change the branching scheme for a Subversion repository.
376 See 'bzr help svn-branching-schemes' for details.
378 takes_args = ['location?']
380 Option('set', help="Change the branching scheme. "),
381 Option('repository-wide',
382 help="Act on repository-wide setting rather than local.")
385 def run(self, location=".", set=False, repository_wide=False):
386 from bzrlib.bzrdir import BzrDir
387 from bzrlib.errors import BzrCommandError
388 from bzrlib.msgeditor import edit_commit_message
389 from bzrlib.repository import Repository
390 from bzrlib.trace import info
391 from bzrlib.plugins.svn.repository import SvnRepository
392 from bzrlib.plugins.svn.mapping3.scheme import scheme_from_branch_list
393 from bzrlib.plugins.svn.mapping3 import config_set_scheme, get_property_scheme, set_property_scheme
394 def scheme_str(scheme):
397 return "".join(map(lambda x: x+"\n", scheme.to_lines()))
398 dir = BzrDir.open_containing(location)[0]
399 repos = dir.find_repository()
400 if not isinstance(repos, SvnRepository):
401 raise BzrCommandError("Not a Subversion repository: %s" % location)
403 scheme = get_property_scheme(repos)
405 scheme = repos.get_mapping().scheme
407 schemestr = edit_commit_message("",
408 start_message=scheme_str(scheme))
409 scheme = scheme_from_branch_list(
410 map(lambda x:x.strip("\n"), schemestr.splitlines()))
412 set_property_scheme(repos, scheme)
414 config_set_scheme(repos, scheme, mandatory=True)
415 elif scheme is not None:
416 info(scheme_str(scheme))
419 register_command(cmd_svn_branching_scheme)
422 class cmd_svn_set_revprops(Command):
423 """Migrate Bazaar metadata to Subversion revision properties.
425 This requires that you have permission to change the
426 revision properties on the repository.
428 To change these permissions, edit the hooks/pre-revprop-change
429 file in the Subversion repository.
431 takes_args = ['location']
433 def run(self, location="."):
434 raise NotImplementedError(self.run)
437 register_command(cmd_svn_set_revprops)
441 """Returns the testsuite for bzr-svn."""
442 from unittest import TestSuite
443 from bzrlib.plugins.svn import tests
445 suite.addTest(tests.test_suite())
449 if __name__ == '__main__':
450 print ("This is a Bazaar plugin. Copy this directory to ~/.bazaar/plugins "
452 elif __name__ != 'bzrlib.plugins.svn':
453 raise ImportError('The Subversion plugin must be installed as'
454 ' bzrlib.plugins.svn not %s' % __name__)