Hide subversion-wc as it's uninitializable.
[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 """Conversion of full repositories."""
17 from bzrlib import ui, urlutils
18 from bzrlib.bzrdir import BzrDir, Converter
19 from bzrlib.branch import Branch
20 from bzrlib.errors import (BzrError, NotBranchError, NoSuchFile, 
21                            NoRepositoryPresent, NoSuchRevision)
22 from bzrlib.revision import ensure_null
23 from bzrlib.transport import get_transport
24
25 from format import get_rich_root_format
26
27 import svn.core, svn.repos
28
29 def transport_makedirs(transport, location_url):
30     """Create missing directories.
31     
32     :param transport: Transport to use.
33     :param location_url: URL for which parents should be created.
34     """
35     needed = [(transport, transport.relpath(location_url))]
36     while needed:
37         try:
38             transport, relpath = needed[-1]
39             transport.mkdir(relpath)
40             needed.pop()
41         except NoSuchFile:
42             if relpath == "":
43                 raise
44             needed.append((transport, urlutils.dirname(relpath)))
45
46
47 class NotDumpFile(BzrError):
48     """A file specified was not a dump file."""
49     _fmt = """%(dumpfile)s is not a dump file."""
50     def __init__(self, dumpfile):
51         BzrError.__init__(self)
52         self.dumpfile = dumpfile
53
54
55 def load_dumpfile(dumpfile, outputdir):
56     """Load a Subversion dump file.
57
58     :param dumpfile: Path to dump file.
59     :param outputdir: Directory in which Subversion repository should be 
60         created.
61     """
62     from cStringIO import StringIO
63     repos = svn.repos.svn_repos_create(outputdir, '', '', None, None)
64     if dumpfile.endswith(".gz"):
65         import gzip
66         file = gzip.GzipFile(dumpfile)
67     elif dumpfile.endswith(".bz2"):
68         import bz2
69         file = bz2.BZ2File(dumpfile)
70     else:
71         file = open(dumpfile)
72     try:
73         svn.repos.load_fs2(repos, file, StringIO(), 
74                 svn.repos.load_uuid_default, '', 0, 0, None)
75     except svn.core.SubversionException, (_, num):
76         if num == svn.core.SVN_ERR_STREAM_MALFORMED_DATA:
77             raise NotDumpFile(dumpfile)
78         raise
79     return repos
80
81
82 def convert_repository(source_repos, output_url, scheme=None, 
83                        create_shared_repo=True, working_trees=False, all=False,
84                        format=None, filter_branch=None):
85     """Convert a Subversion repository and its' branches to a 
86     Bazaar repository.
87
88     :param source_repos: Subversion repository
89     :param output_url: URL to write Bazaar repository to.
90     :param scheme: Branching scheme (object) to use
91     :param create_shared_repo: Whether to create a shared Bazaar repository
92     :param working_trees: Whether to create working trees
93     :param all: Whether old revisions, even those not part of any existing 
94         branches, should be imported
95     :param format: Format to use
96     """
97     assert not all or create_shared_repo
98     if format is None:
99         format = get_rich_root_format()
100     dirs = {}
101     to_transport = get_transport(output_url)
102     def get_dir(path):
103         if dirs.has_key(path):
104             return dirs[path]
105         nt = to_transport.clone(path)
106         try:
107             dirs[path] = BzrDir.open_from_transport(nt)
108         except NotBranchError:
109             transport_makedirs(to_transport, urlutils.join(to_transport.base, path))
110             dirs[path] = format.initialize_on_transport(nt)
111         return dirs[path]
112
113     if scheme is not None:
114         source_repos.set_branching_scheme(scheme)
115
116     if create_shared_repo:
117         try:
118             target_repos = get_dir("").open_repository()
119             assert (source_repos.get_scheme().is_branch("") or 
120                     source_repos.get_scheme().is_tag("") or 
121                     target_repos.is_shared())
122         except NoRepositoryPresent:
123             target_repos = get_dir("").create_repository(shared=True)
124         target_repos.set_make_working_trees(working_trees)
125         if all:
126             source_repos.copy_content_into(target_repos)
127
128     if filter_branch is None:
129         filter_branch = lambda (bp, rev, exists): exists
130
131     existing_branches = [(bp, revnum) for (bp, revnum, _) in 
132             filter(filter_branch,
133                    source_repos.find_branches(source_repos.get_scheme()))]
134
135     source_graph = source_repos.get_graph()
136     pb = ui.ui_factory.nested_progress_bar()
137     try:
138         i = 0
139         for (branch, revnum) in existing_branches:
140             if source_repos.transport.check_path(branch, revnum) == svn.core.svn_node_file:
141                 continue
142             pb.update("%s:%d" % (branch, revnum), i, len(existing_branches))
143             revid = source_repos.generate_revision_id(revnum, branch, 
144                                           str(source_repos.get_scheme()))
145
146             target_dir = get_dir(branch)
147             if not create_shared_repo:
148                 try:
149                     target_dir.open_repository()
150                 except NoRepositoryPresent:
151                     target_dir.create_repository()
152             source_branch_url = urlutils.join(source_repos.base, branch)
153             try:
154                 target_branch = target_dir.open_branch()
155             except NotBranchError:
156                 target_branch = target_dir.create_branch()
157                 target_branch.set_parent(source_branch_url)
158             if revid != target_branch.last_revision():
159                 source_branch = Branch.open(source_branch_url)
160                 # Check if target_branch contains a subset of 
161                 # source_branch. If that is not the case, 
162                 # assume that source_branch has been replaced 
163                 # and remove target_branch
164                 if not source_graph.is_ancestor(
165                         ensure_null(target_branch.last_revision()),
166                         ensure_null(source_branch.last_revision())):
167                     target_branch.set_revision_history([])
168                 target_branch.pull(source_branch)
169             if working_trees and not target_dir.has_workingtree():
170                 target_dir.create_workingtree()
171             i += 1
172     finally:
173         pb.finished()
174     
175
176 class SvnConverter(Converter):
177     """Converts from a Subversion directory to a bzr dir."""
178     def __init__(self, target_format):
179         """Create a SvnConverter.
180         :param target_format: The format the resulting repository should be.
181         """
182         self.target_format = target_format
183
184     def convert(self, to_convert, pb):
185         """See Converter.convert()."""
186         convert_repository(to_convert.open_repository(), to_convert.base, 
187                            format=self.target_format, all=True, pb=pb)