Register subversion-wc as format, but keep it non-initialiable.
[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.lazy_import import lazy_import
25 from bzrlib.trace import warning, mutter
26 from bzrlib.transport import register_lazy_transport, register_transport_proto
27 from bzrlib.repository import InterRepository
28
29 lazy_import(globals(), """
30 import branch
31 import commit
32 import fetch 
33 import format
34 import workingtree
35 """)
36
37 # versions ending in 'exp' mean experimental mappings
38 # versions ending in 'dev' mean development version
39 # versions ending in 'final' mean release (well tested, etc)
40 version_info = (0, 4, 3, 'dev', 0)
41
42 if version_info[3] == 'final':
43     version_string = '%d.%d.%d' % version_info[:3]
44 else:
45     version_string = '%d.%d.%d%s%d' % version_info
46 __version__ = version_string
47
48 COMPATIBLE_BZR_VERSIONS = [(0, 90), (0, 91)]
49
50 def check_bzrlib_version(desired):
51     """Check that bzrlib is compatible.
52
53     If version is < all compatible version, assume incompatible.
54     If version is compatible version + 1, assume compatible, with deprecations
55     Otherwise, assume incompatible.
56     """
57     import bzrlib
58     bzrlib_version = bzrlib.version_info[:2]
59     if (bzrlib_version in desired or 
60         ((bzrlib_version[0], bzrlib_version[1]-1) in desired and 
61          bzrlib.version_info[3] == 'dev')):
62         return
63     if bzrlib_version < desired[0]:
64         warning('Installed bzr version %s is too old to be used with bzr-svn'
65                 ' %s.' % (bzrlib.__version__, __version__))
66         # Not using BzrNewError, because it may not exist.
67         raise Exception, ('Version mismatch', desired)
68     else:
69         warning('bzr-svn is not up to date with installed bzr version %s.'
70                 ' \nThere should be a newer version of bzr-svn available.' 
71                 % (bzrlib.__version__))
72         if not (bzrlib_version[0], bzrlib_version[1]-1) in desired:
73             raise Exception, 'Version mismatch'
74
75 def check_bzrsvn_version():
76     """Warn about use of experimental mappings."""
77     if version_info[3] == "exp":
78         warning('version of bzr-svn is experimental; output may change between revisions')
79
80 def check_subversion_version():
81     """Check that Subversion is compatible.
82
83     """
84     try:
85         import svn.delta
86     except ImportError:
87         warning('No Python bindings for Subversion installed. See the '
88                 'bzr-svn README for details.')
89         raise bzrlib.errors.BzrError("missing python subversion bindings")
90     if (not hasattr(svn.delta, 'svn_delta_invoke_txdelta_window_handler') and 
91         not hasattr(svn.delta, 'tx_invoke_window_handler')):
92         warning('Installed Subversion version does not have updated Python '
93                 'bindings. See the bzr-svn README for details.')
94         raise bzrlib.errors.BzrError("incompatible python subversion bindings")
95
96 check_subversion_version()
97
98 register_transport_proto('svn+ssh://', 
99     help="Access using the Subversion smart server tunneled over SSH.")
100 register_transport_proto('svn+file://', 
101     help="Access of local Subversion repositories.")
102 register_transport_proto('svn+http://',
103     help="Access of Subversion smart servers over HTTP.")
104 register_transport_proto('svn+https://',
105     help="Access of Subversion smart servers over secure HTTP.")
106 register_transport_proto('svn://', 
107     help="Access using the Subversion smart server.")
108 register_lazy_transport('svn://', 'bzrlib.plugins.svn.transport', 
109                         'SvnRaTransport')
110 register_lazy_transport('svn+', 'bzrlib.plugins.svn.transport', 
111                         'SvnRaTransport')
112 topic_registry.register_lazy('svn-branching-schemes', 
113                              'bzrlib.plugins.svn.scheme',
114                              'help_schemes', 'Subversion branching schemes')
115
116 BzrDirFormat.register_control_format(format.SvnFormat)
117 BzrDirFormat.register_control_format(workingtree.SvnWorkingTreeDirFormat)
118 format_registry.register("subversion", format.SvnFormat, 
119                          "Subversion repository. ", 
120                          native=False)
121 format_registry.register("subversion-wc", workingtree.SvnWorkingTreeDirFormat, 
122                          "Subversion working copy. ", 
123                          native=False)
124
125 versions_checked = False
126 def lazy_check_versions():
127     global versions_checked
128     if versions_checked:
129         return
130     versions_checked = True
131     check_bzrlib_version(COMPATIBLE_BZR_VERSIONS)
132     check_bzrsvn_version()
133
134 InterRepository.register_optimiser(fetch.InterFromSvnRepository)
135 InterRepository.register_optimiser(commit.InterToSvnRepository)
136
137 def get_scheme(schemename):
138     """Parse scheme identifier and return a branching scheme."""
139     from scheme import BranchingScheme
140     from bzrlib.errors import BzrCommandError
141     
142     ret = BranchingScheme.find_scheme(schemename)
143     if ret is None:
144         raise BzrCommandError('No such branching scheme %r' % schemename)
145     return ret
146
147
148 class cmd_svn_import(Command):
149     """Convert a Subversion repository to a Bazaar repository.
150     
151     """
152     takes_args = ['from_location', 'to_location?']
153     takes_options = [Option('trees', help='Create working trees.'),
154                      Option('standalone', help='Create standalone branches.'),
155                      Option('all', 
156                          help='Convert all revisions, even those not in '
157                               'current branch history (forbids --standalone).'),
158                      Option('scheme', type=get_scheme,
159                          help='Branching scheme (none, trunk, etc). '
160                               'Default: auto.'),
161                      Option('prefix', type=str, 
162                          help='Only consider branches of which path starts '
163                               'with prefix.')
164                     ]
165
166     @display_command
167     def run(self, from_location, to_location=None, trees=False, 
168             standalone=False, scheme=None, all=False, prefix=None):
169         from bzrlib.errors import NoRepositoryPresent
170         from bzrlib.bzrdir import BzrDir
171         from convert import convert_repository
172         import os
173
174         if to_location is None:
175             to_location = os.path.basename(from_location.rstrip("/\\"))
176
177         if all:
178             # All implies shared repository 
179             # (otherwise there is no repository to store revisions in)
180             standalone = False
181
182         if os.path.isfile(from_location):
183             from convert import load_dumpfile
184             import tempfile
185             tmp_repos = tempfile.mkdtemp(prefix='bzr-svn-dump-')
186             mutter('loading dumpfile %r to %r' % (from_location, tmp_repos))
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__)))