Avoid tracebacks on malformed data errors.
[jelmer/subvertpy.git] / __init__.py
1 # Copyright (C) 2005-2007 Jelmer Vernooij <jelmer@samba.org>
2
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.
7
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.
12
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
16
17 """
18 Support for Subversion branches
19 """
20 import bzrlib
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.trace import warning
25 from bzrlib.transport import register_lazy_transport, register_transport_proto
26
27 import format
28
29 # versions ending in 'exp' mean experimental mappings
30 # versions ending in 'dev' mean development version
31 # versions ending in 'final' mean release (well tested, etc)
32 version_info = (0, 4, 4, 'dev', 0)
33
34 if version_info[3] == 'final':
35     version_string = '%d.%d.%d' % version_info[:3]
36 else:
37     version_string = '%d.%d.%d%s%d' % version_info
38 __version__ = version_string
39
40 COMPATIBLE_BZR_VERSIONS = [(0, 90), (0, 91)]
41
42 def check_bzrlib_version(desired):
43     """Check that bzrlib is compatible.
44
45     If version is < all compatible version, assume incompatible.
46     If version is compatible version + 1, assume compatible, with deprecations
47     Otherwise, assume incompatible.
48     """
49     import bzrlib
50     bzrlib_version = bzrlib.version_info[:2]
51     if (bzrlib_version in desired or 
52         ((bzrlib_version[0], bzrlib_version[1]-1) in desired and 
53          bzrlib.version_info[3] == 'dev')):
54         return
55     if bzrlib_version < desired[0]:
56         warning('Installed bzr version %s is too old to be used with bzr-svn'
57                 ' %s.' % (bzrlib.__version__, __version__))
58         # Not using BzrNewError, because it may not exist.
59         raise Exception, ('Version mismatch', desired)
60     else:
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 Exception, 'Version mismatch'
66
67 def check_bzrsvn_version():
68     """Warn about use of experimental mappings."""
69     if version_info[3] == "exp":
70         warning('version of bzr-svn is experimental; output may change between revisions')
71
72 def check_subversion_version():
73     """Check that Subversion is compatible.
74
75     """
76     try:
77         import svn.delta
78     except ImportError:
79         warning('No Python bindings for Subversion installed. See the '
80                 'bzr-svn README for details.')
81         raise bzrlib.errors.BzrError("missing python subversion bindings")
82     if (not hasattr(svn.delta, 'svn_delta_invoke_txdelta_window_handler') and 
83         not hasattr(svn.delta, 'tx_invoke_window_handler')):
84         warning('Installed Subversion version does not have updated Python '
85                 'bindings. See the bzr-svn README for details.')
86         raise bzrlib.errors.BzrError("incompatible python subversion bindings")
87
88 check_subversion_version()
89
90 register_transport_proto('svn+ssh://', 
91     help="Access using the Subversion smart server tunneled over SSH.")
92 register_transport_proto('svn+file://', 
93     help="Access of local Subversion repositories.")
94 register_transport_proto('svn+http://',
95     help="Access of Subversion smart servers over HTTP.")
96 register_transport_proto('svn+https://',
97     help="Access of Subversion smart servers over secure HTTP.")
98 register_transport_proto('svn://', 
99     help="Access using the Subversion smart server.")
100 register_lazy_transport('svn://', 'bzrlib.plugins.svn.transport', 
101                         'SvnRaTransport')
102 register_lazy_transport('svn+', 'bzrlib.plugins.svn.transport', 
103                         'SvnRaTransport')
104 topic_registry.register_lazy('svn-branching-schemes', 
105                              'bzrlib.plugins.svn.scheme',
106                              'help_schemes', 'Subversion branching schemes')
107
108 BzrDirFormat.register_control_format(format.SvnRemoteFormat)
109 BzrDirFormat.register_control_format(format.SvnWorkingTreeDirFormat)
110 format_registry.register("subversion", format.SvnRemoteFormat, 
111                          "Subversion repository. ", 
112                          native=False)
113 format_registry.register("subversion-wc", format.SvnWorkingTreeDirFormat, 
114                          "Subversion working copy. ", 
115                          native=False)
116
117 versions_checked = False
118 def lazy_check_versions():
119     global versions_checked
120     if versions_checked:
121         return
122     versions_checked = True
123     check_bzrlib_version(COMPATIBLE_BZR_VERSIONS)
124     check_bzrsvn_version()
125
126 optimizers_registered = False
127 def lazy_register_optimizers():
128     global optimizers_registered
129     if optimizers_registered:
130         return
131     from bzrlib.repository import InterRepository
132     import commit
133     import fetch
134     optimizers_registered = True
135     InterRepository.register_optimiser(fetch.InterFromSvnRepository)
136     InterRepository.register_optimiser(commit.InterToSvnRepository)
137
138 def get_scheme(schemename):
139     """Parse scheme identifier and return a branching scheme."""
140     from scheme import BranchingScheme
141     from bzrlib.errors import BzrCommandError
142     
143     ret = BranchingScheme.find_scheme(schemename)
144     if ret is None:
145         raise BzrCommandError('No such branching scheme %r' % schemename)
146     return ret
147
148
149 class cmd_svn_import(Command):
150     """Convert a Subversion repository to a Bazaar repository.
151     
152     """
153     takes_args = ['from_location', 'to_location?']
154     takes_options = [Option('trees', help='Create working trees.'),
155                      Option('standalone', help='Create standalone branches.'),
156                      Option('all', 
157                          help='Convert all revisions, even those not in '
158                               'current branch history (forbids --standalone).'),
159                      Option('scheme', type=get_scheme,
160                          help='Branching scheme (none, trunk, etc). '
161                               'Default: auto.'),
162                      Option('prefix', type=str, 
163                          help='Only consider branches of which path starts '
164                               'with prefix.')
165                     ]
166
167     @display_command
168     def run(self, from_location, to_location=None, trees=False, 
169             standalone=False, scheme=None, all=False, prefix=None):
170         from bzrlib.errors import NoRepositoryPresent
171         from bzrlib.bzrdir import BzrDir
172         from convert import convert_repository
173         import os
174
175         if to_location is None:
176             to_location = os.path.basename(from_location.rstrip("/\\"))
177
178         if all:
179             # All implies shared repository 
180             # (otherwise there is no repository to store revisions in)
181             standalone = False
182
183         if os.path.isfile(from_location):
184             from convert import load_dumpfile
185             import tempfile
186             tmp_repos = tempfile.mkdtemp(prefix='bzr-svn-dump-')
187             load_dumpfile(from_location, tmp_repos)
188             from_location = tmp_repos
189         else:
190             tmp_repos = None
191
192         from_dir = BzrDir.open(from_location)
193         try:
194             from_repos = from_dir.open_repository()
195         except NoRepositoryPresent, e:
196             from bzrlib.errors import BzrCommandError
197             raise BzrCommandError("No Repository found at %s. "
198                 "For individual branches, use 'bzr branch'." % from_location)
199
200         def filter_branch((branch_path, revnum, exists)):
201             if prefix is not None and not branch_path.startswith(prefix):
202                 return False
203             return exists
204
205         convert_repository(from_repos, to_location, scheme, not standalone, 
206                 trees, all, filter_branch=filter_branch)
207
208         if tmp_repos is not None:
209             from bzrlib import osutils
210             osutils.rmtree(tmp_repos)
211
212
213 register_command(cmd_svn_import)
214
215 class cmd_svn_upgrade(Command):
216     """Upgrade revisions mapped from Subversion in a Bazaar branch.
217     
218     This will change the revision ids of revisions whose parents 
219     were mapped from svn revisions.
220     """
221     takes_args = ['from_repository?']
222     takes_options = ['verbose']
223
224     @display_command
225     def run(self, from_repository=None, verbose=False):
226         from upgrade import upgrade_branch, upgrade_workingtree
227         from bzrlib.branch import Branch
228         from bzrlib.errors import NoWorkingTree, BzrCommandError
229         from bzrlib.repository import Repository
230         from bzrlib.workingtree import WorkingTree
231         try:
232             wt_to = WorkingTree.open(".")
233             branch_to = wt_to.branch
234         except NoWorkingTree:
235             wt_to = None
236             branch_to = Branch.open(".")
237
238         stored_loc = branch_to.get_parent()
239         if from_repository is None:
240             if stored_loc is None:
241                 raise BzrCommandError("No pull location known or"
242                                              " specified.")
243             else:
244                 import bzrlib.urlutils as urlutils
245                 display_url = urlutils.unescape_for_display(stored_loc,
246                         self.outf.encoding)
247                 self.outf.write("Using saved location: %s\n" % display_url)
248                 from_repository = Branch.open(stored_loc).repository
249         else:
250             from_repository = Repository.open(from_repository)
251
252         if wt_to is not None:
253             upgrade_workingtree(wt_to, from_repository, allow_changes=True,
254                                 verbose=verbose)
255         else:
256             upgrade_branch(branch_to, from_repository, allow_changes=True, 
257                            verbose=verbose)
258
259         if wt_to is not None:
260             wt_to.set_last_revision(branch_to.last_revision())
261
262 register_command(cmd_svn_upgrade)
263
264 class cmd_svn_push(Command):
265     """Push revisions to Subversion, creating a new branch if necessary.
266
267     The behaviour of this command is the same as that of "bzr push", except 
268     that it also creates new branches.
269     
270     This command is experimental and will be removed in the future when all 
271     functionality is included in "bzr push".
272     """
273     takes_args = ['location']
274     takes_options = ['revision']
275
276     def run(self, location, revision=None):
277         from bzrlib.bzrdir import BzrDir
278         from bzrlib.branch import Branch
279         from bzrlib.errors import NotBranchError, BzrCommandError
280         bzrdir = BzrDir.open(location)
281         source_branch = Branch.open_containing(".")[0]
282         if revision is not None:
283             if len(revision) > 1:
284                 raise BzrCommandError(
285                     'bzr svn-push --revision takes exactly one revision' 
286                     ' identifier')
287             revision_id = revision[0].in_history(source_branch).rev_id
288         else:
289             revision_id = None
290         try:
291             target_branch = bzrdir.open_branch()
292             target_branch.pull(source_branch, revision_id)
293         except NotBranchError:
294             target_branch = bzrdir.import_branch(source_branch, revision_id)
295
296 register_command(cmd_svn_push)
297
298
299 class cmd_svn_branching_scheme(Command):
300     """Show or change the branching scheme for a Subversion repository.
301
302     See 'bzr help svn-branching-scheme' for details.
303     """
304     takes_args = ['location?']
305     takes_options = [
306         Option('set', help="Change the branching scheme. "),
307         Option('repository-wide', 
308             help="Act on repository-wide setting rather than local.")
309         ]
310
311     def run(self, location=".", set=False, repository_wide=False):
312         from bzrlib.msgeditor import edit_commit_message
313         from bzrlib.repository import Repository
314         from bzrlib.trace import info
315         from scheme import scheme_from_branch_list
316         def scheme_str(scheme):
317             if scheme is None:
318                 return ""
319             return "".join(map(lambda x: x+"\n", scheme.to_lines()))
320         repos = Repository.open(location)
321         if repository_wide:
322             scheme = repos._get_property_scheme()
323         else:
324             scheme = repos.get_scheme()
325         if set:
326             schemestr = edit_commit_message("", 
327                                             start_message=scheme_str(scheme))
328             scheme = scheme_from_branch_list(
329                 map(lambda x:x.strip("\n"), schemestr.splitlines()))
330             if repository_wide:
331                 repos.set_property_scheme(scheme)
332             else:
333                 repos.set_branching_scheme(scheme)
334         elif scheme is not None:
335             info(scheme_str(scheme))
336
337
338 register_command(cmd_svn_branching_scheme)
339
340
341 def test_suite():
342     from unittest import TestSuite
343     import tests
344     suite = TestSuite()
345     suite.addTest(tests.test_suite())
346     return suite
347
348
349 if __name__ == '__main__':
350     print ("This is a Bazaar plugin. Copy this directory to ~/.bazaar/plugins "
351           "to use it.\n")
352 elif __name__ != 'bzrlib.plugins.svn':
353     raise ImportError('The Subversion plugin must be installed as'
354                       ' bzrlib.plugins.svn not %s' % __name__)
355 else:
356     import os, sys
357     sys.path.append(os.path.dirname(os.path.abspath(__file__)))