1 # Copyright (C) 2005-2007 by Jelmer Vernooij
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
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
25 from format import get_rich_root_format
27 import svn.core, svn.repos
29 def transport_makedirs(transport, location_url):
30 """Create missing directories.
32 :param transport: Transport to use.
33 :param location_url: URL for which parents should be created.
35 needed = [(transport, transport.relpath(location_url))]
38 transport, relpath = needed[-1]
39 transport.mkdir(relpath)
44 needed.append((transport, urlutils.dirname(relpath)))
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
55 def load_dumpfile(dumpfile, outputdir):
56 """Load a Subversion dump file.
58 :param dumpfile: Path to dump file.
59 :param outputdir: Directory in which Subversion repository should be
62 from cStringIO import StringIO
63 repos = svn.repos.svn_repos_create(outputdir, '', '', None, None)
64 if dumpfile.endswith(".gz"):
66 file = gzip.GzipFile(dumpfile)
67 elif dumpfile.endswith(".bz2"):
69 file = bz2.BZ2File(dumpfile)
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)
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
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
97 assert not all or create_shared_repo
99 format = get_rich_root_format()
101 to_transport = get_transport(output_url)
103 if dirs.has_key(path):
105 nt = to_transport.clone(path)
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)
113 if scheme is not None:
114 source_repos.set_branching_scheme(scheme)
116 if create_shared_repo:
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)
126 source_repos.copy_content_into(target_repos)
128 if filter_branch is None:
129 filter_branch = lambda (bp, rev, exists): exists
131 existing_branches = [(bp, revnum) for (bp, revnum, _) in
132 filter(filter_branch,
133 source_repos.find_branches(source_repos.get_scheme()))]
135 source_graph = source_repos.get_graph()
136 pb = ui.ui_factory.nested_progress_bar()
139 for (branch, revnum) in existing_branches:
140 if source_repos.transport.check_path(branch, revnum) == svn.core.svn_node_file:
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()))
146 target_dir = get_dir(branch)
147 if not create_shared_repo:
149 target_dir.open_repository()
150 except NoRepositoryPresent:
151 target_dir.create_repository()
152 source_branch_url = urlutils.join(source_repos.base, branch)
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()
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.
182 self.target_format = target_format
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)