Merge 0.3.2
[jelmer/subvertpy.git] / convert.py
1 # Copyright (C) 2005-2007 by Jelmer Vernooij
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 import os
18 import tempfile
19
20 from bzrlib.plugin import load_plugins
21 load_plugins()
22
23 from bzrlib.bzrdir import BzrDir, BzrDirFormat
24 from bzrlib.branch import Branch
25 from bzrlib.errors import BzrError, NotBranchError, NoSuchFile, NoRepositoryPresent
26 import bzrlib.osutils as osutils
27 from bzrlib.progress import DummyProgress
28 from bzrlib.repository import Repository
29 from bzrlib.trace import info, mutter
30 from bzrlib.transport import get_transport
31 import bzrlib.urlutils as urlutils
32 from bzrlib.ui import ui_factory
33
34 from format import SvnRemoteAccess, SvnFormat
35 from repository import SvnRepository
36 from transport import SvnRaTransport
37
38 import svn.core
39
40 def transport_makedirs(transport, location_url):
41     needed = [(transport, transport.relpath(location_url))]
42     while needed:
43         try:
44             transport, relpath = needed[-1]
45             transport.mkdir(relpath)
46             needed.pop()
47         except NoSuchFile:
48             if relpath == "":
49                 raise
50             needed.append((transport, urlutils.dirname(relpath)))
51
52
53 class NotDumpFile(BzrError):
54     _fmt = """%(dumpfile)s is not a dump file."""
55     def __init__(self, dumpfile):
56         self.dumpfile = dumpfile
57
58
59 def load_dumpfile(dumpfile, outputdir):
60     import svn
61     from svn.core import SubversionException
62     from cStringIO import StringIO
63     repos = svn.repos.svn_repos_create(outputdir, '', '', None, None)
64     try:
65         file = open(dumpfile)
66         svn.repos.load_fs2(repos, file, StringIO(), 
67                 svn.repos.load_uuid_default, '', 0, 0, None)
68     except SubversionException, (svn.core.SVN_ERR_STREAM_MALFORMED_DATA, _):
69         raise NotDumpFile(dumpfile)
70     return repos
71
72
73 def convert_repository(url, output_url, scheme, create_shared_repo=True, 
74                        working_trees=False, all=False):
75     assert not all or create_shared_repo
76
77
78     if os.path.isfile(url):
79         tmp_repos = tempfile.mkdtemp(prefix='bzr-svn-dump-')
80         mutter('loading dumpfile %r to %r' % (url, tmp_repos))
81         load_dumpfile(url, tmp_repos)
82         url = tmp_repos
83     else:
84         tmp_repos = None
85
86     dirs = {}
87     to_transport = get_transport(output_url)
88     def get_dir(path):
89         if dirs.has_key(path):
90             return dirs[path]
91         nt = to_transport.clone(path)
92         try:
93             dirs[path] = BzrDir.open_from_transport(nt)
94         except NotBranchError:
95             transport_makedirs(to_transport, urlutils.join(to_transport.base, path))
96             dirs[path] = BzrDirFormat.get_default_format().initialize_on_transport(nt)
97         return dirs[path]
98
99     try:
100         source_repos = SvnRepository.open(url)
101         source_repos.set_branching_scheme(scheme)
102
103         if create_shared_repo:
104             try:
105                 target_repos = get_dir("").open_repository()
106                 assert scheme.is_branch("") or scheme.is_tag("") or target_repos.is_shared()
107             except NoRepositoryPresent:
108                 target_repos = get_dir("").create_repository(shared=True)
109             target_repos.set_make_working_trees(working_trees)
110             if all:
111                 source_repos.copy_content_into(target_repos)
112
113         pb = ui_factory.nested_progress_bar()
114         try:
115             branches = source_repos.find_branches(pb=pb)
116             existing_branches = filter(lambda (bp, revnum, exists): exists, 
117                                    branches)
118         finally:
119             pb.finished()
120
121         pb = ui_factory.nested_progress_bar()
122                        
123         try:
124             i = 0
125             for (branch, revnum, exists) in existing_branches:
126                 if source_repos.transport.check_path(branch, revnum) == svn.core.svn_node_file:
127                     continue
128                 pb.update("%s:%d" % (branch, revnum), i, len(existing_branches))
129                 revid = source_repos.generate_revision_id(revnum, branch)
130
131                 target_dir = get_dir(branch)
132                 if not create_shared_repo:
133                     try:
134                         target_dir.open_repository()
135                     except NoRepositoryPresent:
136                         target_dir.create_repository()
137                 try:
138                     target_branch = target_dir.open_branch()
139                 except NotBranchError:
140                     target_branch = target_dir.create_branch()
141                 if not revid in target_branch.revision_history():
142                     source_branch = Branch.open(urlutils.join(url, branch))
143                     # Check if target_branch contains a subset of 
144                     # source_branch. If that is not the case, 
145                     # assume that source_branch has been replaced 
146                     # and remove target_branch
147                     if not target_branch.last_revision() in \
148                             source_branch.revision_history():
149                         target_branch.set_revision_history([])
150
151                     target_branch.pull(source_branch)
152                 if working_trees and not target_dir.has_workingtree():
153                     target_dir.create_workingtree()
154                 i+=1
155         finally:
156             pb.finished()
157     finally:
158         if tmp_repos:
159             osutils.rmtree(tmp_repos)