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 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
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
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, 9, '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, 3)]
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 check_subversion_version()
85 register_transport_proto('svn+ssh://',
86 help="Access using the Subversion smart server tunneled over SSH.")
87 register_transport_proto('svn+file://',
88 help="Access of local Subversion repositories.")
89 register_transport_proto('svn+http://',
90 help="Access of Subversion smart servers over HTTP.")
91 register_transport_proto('svn+https://',
92 help="Access of Subversion smart servers over secure HTTP.")
93 register_transport_proto('svn://',
94 help="Access using the Subversion smart server.")
95 register_lazy_transport('svn://', 'bzrlib.plugins.svn.transport',
97 register_lazy_transport('svn+', 'bzrlib.plugins.svn.transport',
99 topic_registry.register_lazy('svn-branching-schemes',
100 'bzrlib.plugins.svn.scheme',
101 'help_schemes', 'Subversion branching schemes')
103 BzrDirFormat.register_control_format(format.SvnRemoteFormat)
104 BzrDirFormat.register_control_format(format.SvnWorkingTreeDirFormat)
105 format_registry.register("subversion", format.SvnRemoteFormat,
106 "Subversion repository. ",
108 format_registry.register("subversion-wc", format.SvnWorkingTreeDirFormat,
109 "Subversion working copy. ",
110 native=False, hidden=True)
111 SPEC_TYPES.append(revspec.RevisionSpec_svn)
113 versions_checked = False
114 def lazy_check_versions():
115 """Check whether all dependencies have the right versions.
117 :note: Only checks once, caches the result."""
118 global versions_checked
121 versions_checked = True
122 check_bzrlib_version(COMPATIBLE_BZR_VERSIONS)
124 optimizers_registered = False
125 def lazy_register_optimizers():
126 """Register optimizers for fetching between Subversion and Bazaar
129 :note: Only registers on the first call."""
130 global optimizers_registered
131 if optimizers_registered:
133 from bzrlib.repository import InterRepository
136 optimizers_registered = True
137 InterRepository.register_optimiser(fetch.InterFromSvnRepository)
138 InterRepository.register_optimiser(commit.InterToSvnRepository)
141 def get_scheme(schemename):
142 """Parse scheme identifier and return a branching scheme.
144 :param schemename: Name of the scheme to retrieve.
146 if isinstance(schemename, unicode):
147 schemename = schemename.encode("ascii")
148 from scheme import BranchingScheme
149 from bzrlib.errors import BzrCommandError
151 ret = BranchingScheme.find_scheme(schemename)
153 raise BzrCommandError('No such branching scheme %r' % schemename)
157 class cmd_svn_import(Command):
158 """Convert a Subversion repository to a Bazaar repository.
161 takes_args = ['from_location', 'to_location?']
162 takes_options = [Option('trees', help='Create working trees.'),
163 Option('standalone', help='Create standalone branches.'),
165 help='Convert all revisions, even those not in '
166 'current branch history (forbids --standalone).'),
167 Option('scheme', type=get_scheme,
168 help='Branching scheme (none, trunk, etc). '
170 Option('prefix', type=str,
171 help='Only consider branches of which path starts '
176 def run(self, from_location, to_location=None, trees=False,
177 standalone=False, scheme=None, all=False, prefix=None):
178 from bzrlib.branch import Branch
179 from bzrlib.bzrdir import BzrDir
180 from bzrlib.errors import BzrCommandError, NoRepositoryPresent, NotBranchError
181 from bzrlib import urlutils
182 from convert import convert_repository
183 from repository import SvnRepository
186 if to_location is None:
187 to_location = os.path.basename(from_location.rstrip("/\\"))
190 # All implies shared repository
191 # (otherwise there is no repository to store revisions in)
194 if os.path.isfile(from_location):
195 from convert import load_dumpfile
197 tmp_repos = tempfile.mkdtemp(prefix='bzr-svn-dump-')
198 load_dumpfile(from_location, tmp_repos)
199 from_location = tmp_repos
203 from_dir = BzrDir.open(from_location)
205 from_repos = from_dir.open_repository()
206 except NoRepositoryPresent, e:
208 Branch.open(from_location)
209 raise BzrCommandError("No Repository found at %s. "
210 "For individual branches, use 'bzr branch'." % from_location)
211 except NotBranchError:
212 if prefix is not None:
213 raise BzrCommandError("Path inside repository specified and --prefix specified")
214 from_repos = from_dir.find_repository()
215 prefix = urlutils.relative_url(from_repos.base, from_location)
216 self.outf.write("Importing branches below %s\n" %
217 urlutils.unescape_for_display(prefix, self.outf.encoding))
219 if prefix is not None:
220 prefix = prefix.strip("/") + "/"
222 if not isinstance(from_repos, SvnRepository):
223 raise BzrCommandError(
224 "Not a Subversion repository: %s" % from_location)
226 def filter_branch((branch_path, revnum, exists)):
227 if prefix is not None and not branch_path.startswith(prefix):
231 convert_repository(from_repos, to_location, scheme, not standalone,
232 trees, all, filter_branch=filter_branch)
234 if tmp_repos is not None:
235 from bzrlib import osutils
236 osutils.rmtree(tmp_repos)
239 register_command(cmd_svn_import)
241 class cmd_svn_upgrade(Command):
242 """Upgrade revisions mapped from Subversion in a Bazaar branch.
244 This will change the revision ids of revisions whose parents
245 were mapped from svn revisions.
247 takes_args = ['from_repository?']
248 takes_options = ['verbose']
251 def run(self, from_repository=None, verbose=False):
252 from upgrade import upgrade_branch, upgrade_workingtree
253 from bzrlib.branch import Branch
254 from bzrlib.errors import NoWorkingTree, BzrCommandError
255 from bzrlib.repository import Repository
256 from bzrlib.trace import info
257 from bzrlib.workingtree import WorkingTree
259 wt_to = WorkingTree.open(".")
260 branch_to = wt_to.branch
261 except NoWorkingTree:
263 branch_to = Branch.open(".")
265 stored_loc = branch_to.get_parent()
266 if from_repository is None:
267 if stored_loc is None:
268 raise BzrCommandError("No pull location known or"
271 import bzrlib.urlutils as urlutils
272 display_url = urlutils.unescape_for_display(stored_loc,
274 self.outf.write("Using saved location: %s\n" % display_url)
275 from_repository = Branch.open(stored_loc).repository
277 from_repository = Repository.open(from_repository)
279 if wt_to is not None:
280 renames = upgrade_workingtree(wt_to, from_repository,
281 allow_changes=True, verbose=verbose)
283 renames = upgrade_branch(branch_to, from_repository,
284 allow_changes=True, verbose=verbose)
287 info("Nothing to do.")
289 if wt_to is not None:
290 wt_to.set_last_revision(branch_to.last_revision())
292 register_command(cmd_svn_upgrade)
294 class cmd_svn_push(Command):
295 """Push revisions to Subversion, creating a new branch if necessary.
297 The behaviour of this command is the same as that of "bzr push", except
298 that it also creates new branches.
300 This command is experimental and will be removed in the future when all
301 functionality is included in "bzr push".
303 takes_args = ['location?']
304 takes_options = ['revision', 'remember', Option('directory',
305 help='Branch to push from, '
306 'rather than the one containing the working directory.',
311 def run(self, location=None, revision=None, remember=False,
313 from bzrlib.bzrdir import BzrDir
314 from bzrlib.branch import Branch
315 from bzrlib.errors import NotBranchError, BzrCommandError
316 from bzrlib import urlutils
318 if directory is None:
320 source_branch = Branch.open_containing(directory)[0]
321 stored_loc = source_branch.get_push_location()
323 if stored_loc is None:
324 raise BzrCommandError("No push location known or specified.")
326 display_url = urlutils.unescape_for_display(stored_loc,
328 self.outf.write("Using saved location: %s\n" % display_url)
329 location = stored_loc
331 bzrdir = BzrDir.open(location)
332 if revision is not None:
333 if len(revision) > 1:
334 raise BzrCommandError(
335 'bzr svn-push --revision takes exactly one revision'
337 revision_id = revision[0].in_history(source_branch).rev_id
341 target_branch = bzrdir.open_branch()
342 target_branch.pull(source_branch, revision_id)
343 except NotBranchError:
344 target_branch = bzrdir.import_branch(source_branch, revision_id)
345 # We successfully created the target, remember it
346 if source_branch.get_push_location() is None or remember:
347 source_branch.set_push_location(target_branch.base)
349 register_command(cmd_svn_push)
352 class cmd_svn_branching_scheme(Command):
353 """Show or change the branching scheme for a Subversion repository.
355 See 'bzr help svn-branching-schemes' for details.
357 takes_args = ['location?']
359 Option('set', help="Change the branching scheme. "),
360 Option('repository-wide',
361 help="Act on repository-wide setting rather than local.")
364 def run(self, location=".", set=False, repository_wide=False):
365 from bzrlib.bzrdir import BzrDir
366 from bzrlib.errors import BzrCommandError
367 from bzrlib.msgeditor import edit_commit_message
368 from bzrlib.repository import Repository
369 from bzrlib.trace import info
370 from repository import SvnRepository
371 from scheme import scheme_from_branch_list
372 def scheme_str(scheme):
375 return "".join(map(lambda x: x+"\n", scheme.to_lines()))
376 dir = BzrDir.open_containing(location)[0]
377 repos = dir.find_repository()
378 if not isinstance(repos, SvnRepository):
379 raise BzrCommandError("Not a Subversion repository: %s" % location)
381 scheme = repos._get_property_scheme()
383 scheme = repos.get_scheme()
385 schemestr = edit_commit_message("",
386 start_message=scheme_str(scheme))
387 scheme = scheme_from_branch_list(
388 map(lambda x:x.strip("\n"), schemestr.splitlines()))
390 repos.set_property_scheme(scheme)
392 repos.set_branching_scheme(scheme, mandatory=True)
393 elif scheme is not None:
394 info(scheme_str(scheme))
397 register_command(cmd_svn_branching_scheme)
400 class cmd_svn_set_revprops(Command):
401 """Migrate Bazaar metadata to Subversion revision properties.
403 This requires that you have permission to change the
404 revision properties on the repository.
406 To change these permissions, edit the hooks/pre-revprop-change
407 file in the Subversion repository.
409 takes_args = ['location']
411 def run(self, location="."):
412 raise NotImplementedError(self.run)
415 register_command(cmd_svn_set_revprops)
419 """Returns the testsuite for bzr-svn."""
420 from unittest import TestSuite
423 suite.addTest(tests.test_suite())
427 if __name__ == '__main__':
428 print ("This is a Bazaar plugin. Copy this directory to ~/.bazaar/plugins "
430 elif __name__ != 'bzrlib.plugins.svn':
431 raise ImportError('The Subversion plugin must be installed as'
432 ' bzrlib.plugins.svn not %s' % __name__)
435 sys.path.append(os.path.dirname(os.path.abspath(__file__)))