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
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, 10, 'exp', 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)]
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.'
63 % (bzrlib.__version__))
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.
74 warning('No Python bindings for Subversion installed. See the '
75 'bzr-svn README for details.')
76 raise bzrlib.errors.BzrError("missing python subversion bindings")
77 if (not hasattr(svn.delta, 'svn_delta_invoke_txdelta_window_handler') and
78 not hasattr(svn.delta, 'tx_invoke_window_handler')):
79 warning('Installed Subversion version does not have updated Python '
80 'bindings. See the bzr-svn README for details.')
81 raise bzrlib.errors.BzrError("incompatible python subversion bindings")
83 mutter("bzr-svn: using Subversion %d.%d.%d (%s)" % (svn.core.SVN_VER_MAJOR, svn.core.SVN_VER_MINOR, svn.core.SVN_VER_MICRO, svn.core.__file__))
85 check_subversion_version()
87 register_transport_proto('svn+ssh://',
88 help="Access using the Subversion smart server tunneled over SSH.")
89 register_transport_proto('svn+file://',
90 help="Access of local Subversion repositories.")
91 register_transport_proto('svn+http://',
92 help="Access of Subversion smart servers over HTTP.")
93 register_transport_proto('svn+https://',
94 help="Access of Subversion smart servers over secure HTTP.")
95 register_transport_proto('svn://',
96 help="Access using the Subversion smart server.")
97 register_lazy_transport('svn://', 'bzrlib.plugins.svn.transport',
99 register_lazy_transport('svn+', 'bzrlib.plugins.svn.transport',
101 topic_registry.register_lazy('svn-branching-schemes',
102 'bzrlib.plugins.svn.scheme',
103 'help_schemes', 'Subversion branching schemes')
105 BzrDirFormat.register_control_format(format.SvnRemoteFormat)
106 BzrDirFormat.register_control_format(format.SvnWorkingTreeDirFormat)
107 format_registry.register("subversion", format.SvnRemoteFormat,
108 "Subversion repository. ",
110 format_registry.register("subversion-wc", format.SvnWorkingTreeDirFormat,
111 "Subversion working copy. ",
112 native=False, hidden=True)
113 SPEC_TYPES.append(revspec.RevisionSpec_svn)
115 versions_checked = False
116 def lazy_check_versions():
117 """Check whether all dependencies have the right versions.
119 :note: Only checks once, caches the result."""
120 global versions_checked
123 versions_checked = True
124 check_bzrlib_version(COMPATIBLE_BZR_VERSIONS)
126 optimizers_registered = False
127 def lazy_register_optimizers():
128 """Register optimizers for fetching between Subversion and Bazaar
131 :note: Only registers on the first call."""
132 global optimizers_registered
133 if optimizers_registered:
135 from bzrlib.repository import InterRepository
138 optimizers_registered = True
139 InterRepository.register_optimiser(fetch.InterFromSvnRepository)
140 InterRepository.register_optimiser(commit.InterToSvnRepository)
143 def get_scheme(schemename):
144 """Parse scheme identifier and return a branching scheme.
146 :param schemename: Name of the scheme to retrieve.
148 if isinstance(schemename, unicode):
149 schemename = schemename.encode("ascii")
150 from scheme import BranchingScheme
151 from bzrlib.errors import BzrCommandError
153 ret = BranchingScheme.find_scheme(schemename)
155 raise BzrCommandError('No such branching scheme %r' % schemename)
159 class cmd_svn_import(Command):
160 """Convert a Subversion repository to a Bazaar repository.
163 takes_args = ['from_location', 'to_location?']
164 takes_options = [Option('trees', help='Create working trees.'),
165 Option('standalone', help='Create standalone branches.'),
167 help='Convert all revisions, even those not in '
168 'current branch history (forbids --standalone).'),
169 Option('scheme', type=get_scheme,
170 help='Branching scheme (none, trunk, etc). '
172 Option('prefix', type=str,
173 help='Only consider branches of which path starts '
178 def run(self, from_location, to_location=None, trees=False,
179 standalone=False, scheme=None, all=False, prefix=None):
180 from bzrlib.branch import Branch
181 from bzrlib.bzrdir import BzrDir
182 from bzrlib.errors import BzrCommandError, NoRepositoryPresent, NotBranchError
183 from bzrlib import urlutils
184 from convert import convert_repository
185 from repository import SvnRepository
188 if to_location is None:
189 to_location = os.path.basename(from_location.rstrip("/\\"))
192 # All implies shared repository
193 # (otherwise there is no repository to store revisions in)
196 if os.path.isfile(from_location):
197 from convert import load_dumpfile
199 tmp_repos = tempfile.mkdtemp(prefix='bzr-svn-dump-')
200 load_dumpfile(from_location, tmp_repos)
201 from_location = tmp_repos
205 from_dir = BzrDir.open(from_location)
207 from_repos = from_dir.open_repository()
208 except NoRepositoryPresent, e:
210 Branch.open(from_location)
211 raise BzrCommandError("No Repository found at %s. "
212 "For individual branches, use 'bzr branch'." % from_location)
213 except NotBranchError:
214 if prefix is not None:
215 raise BzrCommandError("Path inside repository specified and --prefix specified")
216 from_repos = from_dir.find_repository()
217 prefix = urlutils.relative_url(from_repos.base, from_location)
218 self.outf.write("Importing branches below %s\n" %
219 urlutils.unescape_for_display(prefix, self.outf.encoding))
221 if prefix is not None:
222 prefix = prefix.strip("/") + "/"
224 if not isinstance(from_repos, SvnRepository):
225 raise BzrCommandError(
226 "Not a Subversion repository: %s" % from_location)
228 def filter_branch(branch):
229 if prefix is not None and not branch.get_branch_path().startswith(prefix):
233 convert_repository(from_repos, to_location, scheme, not standalone,
234 trees, all, filter_branch=filter_branch)
236 if tmp_repos is not None:
237 from bzrlib import osutils
238 osutils.rmtree(tmp_repos)
241 register_command(cmd_svn_import)
243 class cmd_svn_upgrade(Command):
244 """Upgrade revisions mapped from Subversion in a Bazaar branch.
246 This will change the revision ids of revisions whose parents
247 were mapped from svn revisions.
249 takes_args = ['from_repository?']
250 takes_options = ['verbose']
253 def run(self, from_repository=None, verbose=False):
254 from upgrade import upgrade_branch, upgrade_workingtree
255 from bzrlib.branch import Branch
256 from bzrlib.errors import NoWorkingTree, BzrCommandError
257 from bzrlib.repository import Repository
258 from bzrlib.trace import info
259 from bzrlib.workingtree import WorkingTree
261 wt_to = WorkingTree.open(".")
262 branch_to = wt_to.branch
263 except NoWorkingTree:
265 branch_to = Branch.open(".")
267 stored_loc = branch_to.get_parent()
268 if from_repository is None:
269 if stored_loc is None:
270 raise BzrCommandError("No pull location known or"
273 import bzrlib.urlutils as urlutils
274 display_url = urlutils.unescape_for_display(stored_loc,
276 self.outf.write("Using saved location: %s\n" % display_url)
277 from_repository = Branch.open(stored_loc).repository
279 from_repository = Repository.open(from_repository)
281 if wt_to is not None:
282 renames = upgrade_workingtree(wt_to, from_repository,
283 allow_changes=True, verbose=verbose)
285 renames = upgrade_branch(branch_to, from_repository,
286 allow_changes=True, verbose=verbose)
289 info("Nothing to do.")
291 if wt_to is not None:
292 wt_to.set_last_revision(branch_to.last_revision())
294 register_command(cmd_svn_upgrade)
296 class cmd_svn_push(Command):
297 """Push revisions to Subversion, creating a new branch if necessary.
299 The behaviour of this command is the same as that of "bzr push", except
300 that it also creates new branches.
302 This command is experimental and will be removed in the future when all
303 functionality is included in "bzr push".
305 takes_args = ['location?']
306 takes_options = ['revision', 'remember', Option('directory',
307 help='Branch to push from, '
308 'rather than the one containing the working directory.',
313 def run(self, location=None, revision=None, remember=False,
315 from bzrlib.bzrdir import BzrDir
316 from bzrlib.branch import Branch
317 from bzrlib.errors import NotBranchError, BzrCommandError
318 from bzrlib import urlutils
320 if directory is None:
322 source_branch = Branch.open_containing(directory)[0]
323 stored_loc = source_branch.get_push_location()
325 if stored_loc is None:
326 raise BzrCommandError("No push location known or specified.")
328 display_url = urlutils.unescape_for_display(stored_loc,
330 self.outf.write("Using saved location: %s\n" % display_url)
331 location = stored_loc
333 bzrdir = BzrDir.open(location)
334 if revision is not None:
335 if len(revision) > 1:
336 raise BzrCommandError(
337 'bzr svn-push --revision takes exactly one revision'
339 revision_id = revision[0].in_history(source_branch).rev_id
343 target_branch = bzrdir.open_branch()
344 target_branch.pull(source_branch, revision_id)
345 except NotBranchError:
346 target_branch = bzrdir.import_branch(source_branch, revision_id)
347 # We successfully created the target, remember it
348 if source_branch.get_push_location() is None or remember:
349 source_branch.set_push_location(target_branch.base)
351 register_command(cmd_svn_push)
354 class cmd_svn_branching_scheme(Command):
355 """Show or change the branching scheme for a Subversion repository.
357 See 'bzr help svn-branching-schemes' for details.
359 takes_args = ['location?']
361 Option('set', help="Change the branching scheme. "),
362 Option('repository-wide',
363 help="Act on repository-wide setting rather than local.")
366 def run(self, location=".", set=False, repository_wide=False):
367 from bzrlib.bzrdir import BzrDir
368 from bzrlib.errors import BzrCommandError
369 from bzrlib.msgeditor import edit_commit_message
370 from bzrlib.repository import Repository
371 from bzrlib.trace import info
372 from repository import SvnRepository
373 from scheme import scheme_from_branch_list
374 def scheme_str(scheme):
377 return "".join(map(lambda x: x+"\n", scheme.to_lines()))
378 dir = BzrDir.open_containing(location)[0]
379 repos = dir.find_repository()
380 if not isinstance(repos, SvnRepository):
381 raise BzrCommandError("Not a Subversion repository: %s" % location)
383 scheme = repos._get_property_scheme()
385 scheme = repos.get_mapping().scheme
387 schemestr = edit_commit_message("",
388 start_message=scheme_str(scheme))
389 scheme = scheme_from_branch_list(
390 map(lambda x:x.strip("\n"), schemestr.splitlines()))
392 repos.set_property_scheme(scheme)
394 repos.set_branching_scheme(scheme, mandatory=True)
395 elif scheme is not None:
396 info(scheme_str(scheme))
399 register_command(cmd_svn_branching_scheme)
402 class cmd_svn_set_revprops(Command):
403 """Migrate Bazaar metadata to Subversion revision properties.
405 This requires that you have permission to change the
406 revision properties on the repository.
408 To change these permissions, edit the hooks/pre-revprop-change
409 file in the Subversion repository.
411 takes_args = ['location']
413 def run(self, location="."):
414 raise NotImplementedError(self.run)
417 register_command(cmd_svn_set_revprops)
421 """Returns the testsuite for bzr-svn."""
422 from unittest import TestSuite
425 suite.addTest(tests.test_suite())
429 if __name__ == '__main__':
430 print ("This is a Bazaar plugin. Copy this directory to ~/.bazaar/plugins "
432 elif __name__ != 'bzrlib.plugins.svn':
433 raise ImportError('The Subversion plugin must be installed as'
434 ' bzrlib.plugins.svn not %s' % __name__)
437 sys.path.append(os.path.dirname(os.path.abspath(__file__)))