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 from scheme import BranchingScheme
147 from bzrlib.errors import BzrCommandError
149 ret = BranchingScheme.find_scheme(schemename)
151 raise BzrCommandError('No such branching scheme %r' % schemename)
155 class cmd_svn_import(Command):
156 """Convert a Subversion repository to a Bazaar repository.
159 takes_args = ['from_location', 'to_location?']
160 takes_options = [Option('trees', help='Create working trees.'),
161 Option('standalone', help='Create standalone branches.'),
163 help='Convert all revisions, even those not in '
164 'current branch history (forbids --standalone).'),
165 Option('scheme', type=get_scheme,
166 help='Branching scheme (none, trunk, etc). '
168 Option('prefix', type=str,
169 help='Only consider branches of which path starts '
174 def run(self, from_location, to_location=None, trees=False,
175 standalone=False, scheme=None, all=False, prefix=None):
176 from bzrlib.errors import BzrCommandError, NoRepositoryPresent
177 from bzrlib.bzrdir import BzrDir
178 from convert import convert_repository
179 from repository import SvnRepository
182 if to_location is None:
183 to_location = os.path.basename(from_location.rstrip("/\\"))
186 # All implies shared repository
187 # (otherwise there is no repository to store revisions in)
190 if os.path.isfile(from_location):
191 from convert import load_dumpfile
193 tmp_repos = tempfile.mkdtemp(prefix='bzr-svn-dump-')
194 load_dumpfile(from_location, tmp_repos)
195 from_location = tmp_repos
199 from_dir = BzrDir.open(from_location)
201 from_repos = from_dir.open_repository()
202 except NoRepositoryPresent, e:
203 raise BzrCommandError("No Repository found at %s. "
204 "For individual branches, use 'bzr branch'." % from_location)
206 if not isinstance(from_repos, SvnRepository):
207 raise BzrCommandError(
208 "Not a Subversion repository: %s" % from_location)
210 def filter_branch((branch_path, revnum, exists)):
211 if prefix is not None and not branch_path.startswith(prefix):
215 convert_repository(from_repos, to_location, scheme, not standalone,
216 trees, all, filter_branch=filter_branch)
218 if tmp_repos is not None:
219 from bzrlib import osutils
220 osutils.rmtree(tmp_repos)
223 register_command(cmd_svn_import)
225 class cmd_svn_upgrade(Command):
226 """Upgrade revisions mapped from Subversion in a Bazaar branch.
228 This will change the revision ids of revisions whose parents
229 were mapped from svn revisions.
231 takes_args = ['from_repository?']
232 takes_options = ['verbose']
235 def run(self, from_repository=None, verbose=False):
236 from upgrade import upgrade_branch, upgrade_workingtree
237 from bzrlib.branch import Branch
238 from bzrlib.errors import NoWorkingTree, BzrCommandError
239 from bzrlib.repository import Repository
240 from bzrlib.trace import info
241 from bzrlib.workingtree import WorkingTree
243 wt_to = WorkingTree.open(".")
244 branch_to = wt_to.branch
245 except NoWorkingTree:
247 branch_to = Branch.open(".")
249 stored_loc = branch_to.get_parent()
250 if from_repository is None:
251 if stored_loc is None:
252 raise BzrCommandError("No pull location known or"
255 import bzrlib.urlutils as urlutils
256 display_url = urlutils.unescape_for_display(stored_loc,
258 self.outf.write("Using saved location: %s\n" % display_url)
259 from_repository = Branch.open(stored_loc).repository
261 from_repository = Repository.open(from_repository)
263 if wt_to is not None:
264 renames = upgrade_workingtree(wt_to, from_repository,
265 allow_changes=True, verbose=verbose)
267 renames = upgrade_branch(branch_to, from_repository,
268 allow_changes=True, verbose=verbose)
271 info("Nothing to do.")
273 if wt_to is not None:
274 wt_to.set_last_revision(branch_to.last_revision())
276 register_command(cmd_svn_upgrade)
278 class cmd_svn_push(Command):
279 """Push revisions to Subversion, creating a new branch if necessary.
281 The behaviour of this command is the same as that of "bzr push", except
282 that it also creates new branches.
284 This command is experimental and will be removed in the future when all
285 functionality is included in "bzr push".
287 takes_args = ['location?']
288 takes_options = ['revision', 'remember', Option('directory',
289 help='Branch to push from, '
290 'rather than the one containing the working directory.',
295 def run(self, location=None, revision=None, remember=False,
297 from bzrlib.bzrdir import BzrDir
298 from bzrlib.branch import Branch
299 from bzrlib.errors import NotBranchError, BzrCommandError
300 from bzrlib import urlutils
302 if directory is None:
304 source_branch = Branch.open_containing(directory)[0]
305 stored_loc = source_branch.get_push_location()
307 if stored_loc is None:
308 raise BzrCommandError("No push location known or specified.")
310 display_url = urlutils.unescape_for_display(stored_loc,
312 self.outf.write("Using saved location: %s\n" % display_url)
313 location = stored_loc
315 bzrdir = BzrDir.open(location)
316 if revision is not None:
317 if len(revision) > 1:
318 raise BzrCommandError(
319 'bzr svn-push --revision takes exactly one revision'
321 revision_id = revision[0].in_history(source_branch).rev_id
325 target_branch = bzrdir.open_branch()
326 target_branch.pull(source_branch, revision_id)
327 except NotBranchError:
328 target_branch = bzrdir.import_branch(source_branch, revision_id)
329 # We successfully created the target, remember it
330 if source_branch.get_push_location() is None or remember:
331 source_branch.set_push_location(target_branch.base)
333 register_command(cmd_svn_push)
336 class cmd_svn_branching_scheme(Command):
337 """Show or change the branching scheme for a Subversion repository.
339 See 'bzr help svn-branching-schemes' for details.
341 takes_args = ['location?']
343 Option('set', help="Change the branching scheme. "),
344 Option('repository-wide',
345 help="Act on repository-wide setting rather than local.")
348 def run(self, location=".", set=False, repository_wide=False):
349 from bzrlib.bzrdir import BzrDir
350 from bzrlib.errors import BzrCommandError
351 from bzrlib.msgeditor import edit_commit_message
352 from bzrlib.repository import Repository
353 from bzrlib.trace import info
354 from repository import SvnRepository
355 from scheme import scheme_from_branch_list
356 def scheme_str(scheme):
359 return "".join(map(lambda x: x+"\n", scheme.to_lines()))
360 dir = BzrDir.open_containing(location)[0]
361 repos = dir.find_repository()
362 if not isinstance(repos, SvnRepository):
363 raise BzrCommandError("Not a Subversion repository: %s" % location)
365 scheme = repos._get_property_scheme()
367 scheme = repos.get_scheme()
369 schemestr = edit_commit_message("",
370 start_message=scheme_str(scheme))
371 scheme = scheme_from_branch_list(
372 map(lambda x:x.strip("\n"), schemestr.splitlines()))
374 repos.set_property_scheme(scheme)
376 repos.set_branching_scheme(scheme, mandatory=True)
377 elif scheme is not None:
378 info(scheme_str(scheme))
381 register_command(cmd_svn_branching_scheme)
384 class cmd_svn_set_revprops(Command):
385 """Migrate Bazaar metadata to Subversion revision properties.
387 This requires that you have permission to change the
388 revision properties on the repository.
390 To change these permissions, edit the hooks/pre-revprop-change
391 file in the Subversion repository.
393 takes_args = ['location']
395 def run(self, location="."):
396 raise NotImplementedError(self.run)
399 register_command(cmd_svn_set_revprops)
403 """Returns the testsuite for bzr-svn."""
404 from unittest import TestSuite
407 suite.addTest(tests.test_suite())
411 if __name__ == '__main__':
412 print ("This is a Bazaar plugin. Copy this directory to ~/.bazaar/plugins "
414 elif __name__ != 'bzrlib.plugins.svn':
415 raise ImportError('The Subversion plugin must be installed as'
416 ' bzrlib.plugins.svn not %s' % __name__)
419 sys.path.append(os.path.dirname(os.path.abspath(__file__)))