1 # Copyright (C) 2005-2008 Jelmer Vernooij <jelmer@samba.org>
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 3 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, see <http://www.gnu.org/licenses/>.
16 from bzrlib import osutils, ui
17 from bzrlib.errors import InvalidRevisionId
18 from bzrlib.trace import mutter
19 from bzrlib.plugins.svn import mapping
20 from layout import RepositoryLayout
21 from mapping3.scheme import (BranchingScheme, guess_scheme_from_branch_path,
22 guess_scheme_from_history, ListBranchingScheme,
23 parse_list_scheme_text)
26 SVN_PROP_BZR_BRANCHING_SCHEME = 'bzr:branching-scheme'
28 # Number of revisions to evaluate when guessing the branching scheme
29 SCHEME_GUESS_SAMPLE_SIZE = 2000
31 class SchemeDerivedLayout(RepositoryLayout):
32 def __init__(self, repository, scheme):
33 self.repository = repository
36 def parse(self, path):
37 (bp, rp) = self.scheme.unprefix(path)
38 if self.scheme.is_tag(bp):
42 return (type, "", bp, rp)
44 def get_branches(self):
45 for (bp, _, _) in filter(lambda (bp, rev, exists): exists,
46 self.repository.find_branchpaths(self.scheme)):
47 yield "", bp, bp.split("/")[-1]
49 def is_branch_parent(self, path):
51 return self.scheme.is_branch_parent(path)
53 def is_tag_parent(self, path):
55 return self.scheme.is_tag_parent(path)
59 def get_stored_scheme(repository):
60 """Retrieve the stored branching scheme, either in the repository
61 or in the configuration file.
63 scheme = repository.get_config().get_branching_scheme()
64 if scheme is not None:
65 return (scheme, repository.get_config().branching_scheme_is_mandatory())
67 last_revnum = repository.get_latest_revnum()
68 scheme = get_property_scheme(repository, last_revnum)
69 if scheme is not None:
74 def get_property_scheme(repository, revnum=None):
76 revnum = repository.get_latest_revnum()
77 text = repository.branchprop_list.get_properties("", revnum).get(SVN_PROP_BZR_BRANCHING_SCHEME, None)
80 return ListBranchingScheme(parse_list_scheme_text(text))
82 def set_property_scheme(repository, scheme):
83 def done(revmetadata, pool):
85 editor = repository.transport.get_commit_editor(
86 {svn.core.SVN_PROP_REVISION_LOG: "Updating branching scheme for Bazaar."},
88 root = editor.open_root(-1)
89 editor.change_dir_prop(root, SVN_PROP_BZR_BRANCHING_SCHEME,
90 "".join(map(lambda x: x+"\n", scheme.branch_list)).encode("utf-8"))
91 editor.close_directory(root)
94 def repository_guess_scheme(repository, last_revnum, branch_path=None):
95 pb = ui.ui_factory.nested_progress_bar()
97 scheme = guess_scheme_from_history(
98 repository._log.iter_changes("", last_revnum, max(0, last_revnum-SCHEME_GUESS_SAMPLE_SIZE), pb=pb), last_revnum, branch_path)
101 mutter("Guessed branching scheme: %r" % scheme)
104 def set_branching_scheme(repository, scheme, mandatory=False):
105 repository.get_mapping().scheme = scheme
106 repository.get_config().set_branching_scheme(str(scheme),
110 class BzrSvnMappingv3(mapping.BzrSvnMapping):
111 """The third version of the mappings as used in the 0.4.x series.
115 upgrade_suffix = "-svn3"
116 revid_prefix = "svn-v3-"
118 def __init__(self, scheme):
119 mapping.BzrSvnMapping.__init__(self)
121 assert not isinstance(scheme, str)
123 def get_mandated_layout(self, repository):
124 return SchemeDerivedLayout(repository, self.scheme)
127 def from_repository(cls, repository, _hinted_branch_path=None):
128 (scheme, mandatory) = get_stored_scheme(repository)
132 if scheme is not None:
133 if (_hinted_branch_path is None or
134 scheme.is_branch(_hinted_branch_path)):
137 last_revnum = repository.get_latest_revnum()
138 scheme = repository_guess_scheme(repository, last_revnum, _hinted_branch_path)
140 set_branching_scheme(repository, scheme, mandatory=False)
145 return "%s(%r)" % (self.__class__.__name__, self.scheme)
147 def generate_file_id(self, uuid, revnum, branch, inv_path):
148 assert isinstance(uuid, str)
149 assert isinstance(revnum, int)
150 assert isinstance(branch, str)
151 assert isinstance(inv_path, unicode)
152 inv_path = inv_path.encode("utf-8")
153 ret = "%d@%s:%s:%s" % (revnum, uuid, mapping.escape_svn_path(branch),
154 mapping.escape_svn_path(inv_path))
156 ret = "%d@%s:%s;%s" % (revnum, uuid,
157 mapping.escape_svn_path(branch),
158 sha.new(inv_path).hexdigest())
159 assert isinstance(ret, str)
160 return osutils.safe_file_id(ret)
163 def supports_roundtripping():
167 def _parse_revision_id(cls, revid):
168 assert isinstance(revid, str)
170 if not revid.startswith(cls.revid_prefix):
171 raise InvalidRevisionId(revid, "")
174 (version, uuid, branch_path, srevnum) = revid.split(":")
176 raise InvalidRevisionId(revid, "")
178 scheme = version[len(cls.revid_prefix):]
180 branch_path = mapping.unescape_svn_path(branch_path)
182 return (uuid, branch_path, int(srevnum), scheme)
185 def parse_revision_id(cls, revid):
186 (uuid, branch_path, srevnum, scheme) = cls._parse_revision_id(revid)
187 # Some older versions of bzr-svn 0.4 did not always set a branching
188 # scheme but set "undefined" instead.
189 if scheme == "undefined":
190 scheme = guess_scheme_from_branch_path(branch_path)
192 scheme = BranchingScheme.find_scheme(scheme)
194 return (uuid, branch_path, srevnum, cls(scheme))
196 def is_branch(self, branch_path):
197 return (self.scheme.is_branch(branch_path) or
198 self.scheme.is_tag(branch_path))
200 def is_tag(self, tag_path):
201 return self.scheme.is_tag(tag_path)
204 def _generate_revision_id(cls, uuid, revnum, path, scheme):
205 assert isinstance(revnum, int)
206 assert isinstance(path, str)
208 assert revnum > 0 or path == "", \
209 "Trying to generate revid for (%r,%r)" % (path, revnum)
210 return "%s%s:%s:%s:%d" % (cls.revid_prefix, scheme, uuid, \
211 mapping.escape_svn_path(path.strip("/")), revnum)
213 def generate_revision_id(self, uuid, revnum, path):
214 return self._generate_revision_id(uuid, revnum, path, self.scheme)
216 def unprefix(self, branch_path, repos_path):
217 (bp, np) = self.scheme.unprefix(repos_path)
218 assert branch_path == bp
221 def __eq__(self, other):
222 return type(self) == type(other) and self.scheme == other.scheme
224 class BzrSvnMappingv3FileProps(mapping.BzrSvnMappingFileProps, BzrSvnMappingv3):
228 class BzrSvnMappingv3RevProps(mapping.BzrSvnMappingRevProps, BzrSvnMappingv3):
232 class BzrSvnMappingv3Hybrid(BzrSvnMappingv3):
233 def __init__(self, scheme):
234 BzrSvnMappingv3.__init__(self, scheme)
235 self.revprops = BzrSvnMappingv3RevProps(scheme)
236 self.fileprops = BzrSvnMappingv3FileProps(scheme)
238 def get_rhs_parents(self, branch_path, svn_revprops, fileprops):
239 if svn_revprops.has_key(mapping.SVN_REVPROP_BZR_MAPPING_VERSION):
240 return self.revprops.get_rhs_parents(branch_path, svn_revprops, fileprops)
242 return self.fileprops.get_rhs_parents(branch_path, svn_revprops, fileprops)
244 def get_revision_id(self, branch_path, revprops, fileprops):
245 if revprops.has_key(mapping.SVN_REVPROP_BZR_MAPPING_VERSION):
246 return self.revprops.get_revision_id(branch_path, revprops, fileprops)
248 return self.fileprops.get_revision_id(branch_path, revprops, fileprops)
250 def import_fileid_map(self, svn_revprops, fileprops):
251 if svn_revprops.has_key(mapping.SVN_REVPROP_BZR_MAPPING_VERSION):
252 return self.revprops.import_fileid_map(svn_revprops, fileprops)
254 return self.fileprops.import_fileid_map(svn_revprops, fileprops)
256 def export_revision(self, branch_root, timestamp, timezone, committer, revprops, revision_id,
257 revno, merges, fileprops):
258 (_, fileprops) = self.fileprops.export_revision(branch_root, timestamp, timezone, committer,
259 revprops, revision_id, revno, merges, fileprops)
260 (revprops, _) = self.revprops.export_revision(branch_root, timestamp, timezone, committer,
261 revprops, revision_id, revno, merges, fileprops)
262 return (revprops, fileprops)
264 def export_fileid_map(self, fileids, revprops, fileprops):
265 self.fileprops.export_fileid_map(fileids, revprops, fileprops)
266 self.revprops.export_fileid_map(fileids, revprops, fileprops)
268 def import_revision(self, svn_revprops, fileprops, rev):
269 self.fileprops.import_revision(svn_revprops, fileprops, rev)
270 self.revprops.import_revision(svn_revprops, fileprops, rev)