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 = [(0, 93), (1, 0), (1, 1), (1, 2)]
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.errors import BzrCommandError, NoRepositoryPresent
179 from bzrlib.bzrdir import BzrDir
180 from convert import convert_repository
181 from repository import SvnRepository
184 if to_location is None:
185 to_location = os.path.basename(from_location.rstrip("/\\"))
188 # All implies shared repository
189 # (otherwise there is no repository to store revisions in)
192 if os.path.isfile(from_location):
193 from convert import load_dumpfile
195 tmp_repos = tempfile.mkdtemp(prefix='bzr-svn-dump-')
196 load_dumpfile(from_location, tmp_repos)
197 from_location = tmp_repos
201 from_dir = BzrDir.open(from_location)
203 from_repos = from_dir.open_repository()
204 except NoRepositoryPresent, e:
205 raise BzrCommandError("No Repository found at %s. "
206 "For individual branches, use 'bzr branch'." % from_location)
208 if not isinstance(from_repos, SvnRepository):
209 raise BzrCommandError(
210 "Not a Subversion repository: %s" % from_location)
212 def filter_branch((branch_path, revnum, exists)):
213 if prefix is not None and not branch_path.startswith(prefix):
217 convert_repository(from_repos, to_location, scheme, not standalone,
218 trees, all, filter_branch=filter_branch)
220 if tmp_repos is not None:
221 from bzrlib import osutils
222 osutils.rmtree(tmp_repos)
225 register_command(cmd_svn_import)
227 class cmd_svn_upgrade(Command):
228 """Upgrade revisions mapped from Subversion in a Bazaar branch.
230 This will change the revision ids of revisions whose parents
231 were mapped from svn revisions.
233 takes_args = ['from_repository?']
234 takes_options = ['verbose']
237 def run(self, from_repository=None, verbose=False):
238 from upgrade import upgrade_branch, upgrade_workingtree
239 from bzrlib.branch import Branch
240 from bzrlib.errors import NoWorkingTree, BzrCommandError
241 from bzrlib.repository import Repository
242 from bzrlib.trace import info
243 from bzrlib.workingtree import WorkingTree
245 wt_to = WorkingTree.open(".")
246 branch_to = wt_to.branch
247 except NoWorkingTree:
249 branch_to = Branch.open(".")
251 stored_loc = branch_to.get_parent()
252 if from_repository is None:
253 if stored_loc is None:
254 raise BzrCommandError("No pull location known or"
257 import bzrlib.urlutils as urlutils
258 display_url = urlutils.unescape_for_display(stored_loc,
260 self.outf.write("Using saved location: %s\n" % display_url)
261 from_repository = Branch.open(stored_loc).repository
263 from_repository = Repository.open(from_repository)
265 if wt_to is not None:
266 renames = upgrade_workingtree(wt_to, from_repository,
267 allow_changes=True, verbose=verbose)
269 renames = upgrade_branch(branch_to, from_repository,
270 allow_changes=True, verbose=verbose)
273 info("Nothing to do.")
275 if wt_to is not None:
276 wt_to.set_last_revision(branch_to.last_revision())
278 register_command(cmd_svn_upgrade)
280 class cmd_svn_push(Command):
281 """Push revisions to Subversion, creating a new branch if necessary.
283 The behaviour of this command is the same as that of "bzr push", except
284 that it also creates new branches.
286 This command is experimental and will be removed in the future when all
287 functionality is included in "bzr push".
289 takes_args = ['location?']
290 takes_options = ['revision', 'remember', Option('directory',
291 help='Branch to push from, '
292 'rather than the one containing the working directory.',
297 def run(self, location=None, revision=None, remember=False,
299 from bzrlib.bzrdir import BzrDir
300 from bzrlib.branch import Branch
301 from bzrlib.errors import NotBranchError, BzrCommandError
302 from bzrlib import urlutils
304 if directory is None:
306 source_branch = Branch.open_containing(directory)[0]
307 stored_loc = source_branch.get_push_location()
309 if stored_loc is None:
310 raise BzrCommandError("No push location known or specified.")
312 display_url = urlutils.unescape_for_display(stored_loc,
314 self.outf.write("Using saved location: %s\n" % display_url)
315 location = stored_loc
317 bzrdir = BzrDir.open(location)
318 if revision is not None:
319 if len(revision) > 1:
320 raise BzrCommandError(
321 'bzr svn-push --revision takes exactly one revision'
323 revision_id = revision[0].in_history(source_branch).rev_id
327 target_branch = bzrdir.open_branch()
328 target_branch.pull(source_branch, revision_id)
329 except NotBranchError:
330 target_branch = bzrdir.import_branch(source_branch, revision_id)
331 # We successfully created the target, remember it
332 if source_branch.get_push_location() is None or remember:
333 source_branch.set_push_location(target_branch.base)
335 register_command(cmd_svn_push)
338 class cmd_svn_branching_scheme(Command):
339 """Show or change the branching scheme for a Subversion repository.
341 See 'bzr help svn-branching-schemes' for details.
343 takes_args = ['location?']
345 Option('set', help="Change the branching scheme. "),
346 Option('repository-wide',
347 help="Act on repository-wide setting rather than local.")
350 def run(self, location=".", set=False, repository_wide=False):
351 from bzrlib.bzrdir import BzrDir
352 from bzrlib.errors import BzrCommandError
353 from bzrlib.msgeditor import edit_commit_message
354 from bzrlib.repository import Repository
355 from bzrlib.trace import info
356 from repository import SvnRepository
357 from scheme import scheme_from_branch_list
358 def scheme_str(scheme):
361 return "".join(map(lambda x: x+"\n", scheme.to_lines()))
362 dir = BzrDir.open_containing(location)[0]
363 repos = dir.find_repository()
364 if not isinstance(repos, SvnRepository):
365 raise BzrCommandError("Not a Subversion repository: %s" % location)
367 scheme = repos._get_property_scheme()
369 scheme = repos.get_scheme()
371 schemestr = edit_commit_message("",
372 start_message=scheme_str(scheme))
373 scheme = scheme_from_branch_list(
374 map(lambda x:x.strip("\n"), schemestr.splitlines()))
376 repos.set_property_scheme(scheme)
378 repos.set_branching_scheme(scheme, mandatory=True)
379 elif scheme is not None:
380 info(scheme_str(scheme))
383 register_command(cmd_svn_branching_scheme)
386 class cmd_svn_set_revprops(Command):
387 """Migrate Bazaar metadata to Subversion revision properties.
389 This requires that you have permission to change the
390 revision properties on the repository.
392 To change these permissions, edit the hooks/pre-revprop-change
393 file in the Subversion repository.
395 takes_args = ['location']
397 def run(self, location="."):
398 raise NotImplementedError(self.run)
401 register_command(cmd_svn_set_revprops)
405 """Returns the testsuite for bzr-svn."""
406 from unittest import TestSuite
409 suite.addTest(tests.test_suite())
413 if __name__ == '__main__':
414 print ("This is a Bazaar plugin. Copy this directory to ~/.bazaar/plugins "
416 elif __name__ != 'bzrlib.plugins.svn':
417 raise ImportError('The Subversion plugin must be installed as'
418 ' bzrlib.plugins.svn not %s' % __name__)
421 sys.path.append(os.path.dirname(os.path.abspath(__file__)))