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 core, constants, mapping, properties
20 from bzrlib.plugins.svn.layout import RepositoryLayout
21 from bzrlib.plugins.svn.mapping3.scheme import (BranchingScheme, guess_scheme_from_branch_path,
22 guess_scheme_from_history, ListBranchingScheme,
23 parse_list_scheme_text, NoBranchingScheme,
24 TrunkBranchingScheme, ListBranchingScheme)
27 SVN_PROP_BZR_BRANCHING_SCHEME = 'bzr:branching-scheme'
29 # Number of revisions to evaluate when guessing the branching scheme
30 SCHEME_GUESS_SAMPLE_SIZE = 2000
32 def expand_branch_pattern(begin, todo, check_path, get_children):
33 path = "/".join(begin)
39 if not "*" in todo[0]:
40 return expand_branch_pattern(begin+[todo[0]], todo[1:], check_path, get_children)
41 children = get_children(path)
47 ret.append("/".join(begin+[c]))
49 ret += expand_branch_pattern(begin+[c], todo[1:], check_path, get_children)
53 class SchemeDerivedLayout(RepositoryLayout):
54 def __init__(self, repository, scheme):
55 self.repository = repository
58 def parse(self, path):
59 (bp, rp) = self.scheme.unprefix(path)
60 if self.scheme.is_tag(bp):
64 return (type, "", bp, rp)
66 def get_branches(self, revnum, project=""):
68 return self.repository.transport.check_path(path, revnum) == core.NODE_DIR
69 def find_children(path):
71 assert not path.startswith("/")
72 dirents = self.repository.transport.get_dir(path, revnum)[0]
73 except core.SubversionException, (msg, num):
74 if num == constants.ERR_FS_NOT_DIRECTORY:
76 if num == constants.ERR_FS_NOT_FOUND:
81 for pattern in self.scheme.branch_list:
82 for bp in expand_branch_pattern([], pattern.split("/"), check_path,
84 yield "", bp, bp.split("/")[-1]
86 def is_branch_parent(self, path):
88 return self.scheme.is_branch_parent(path)
90 def is_tag_parent(self, path):
92 return self.scheme.is_tag_parent(path)
95 def get_stored_scheme(repository):
96 """Retrieve the stored branching scheme, either in the repository
97 or in the configuration file.
99 scheme = repository.get_config().get_branching_scheme()
100 if scheme is not None:
101 return (scheme, repository.get_config().branching_scheme_is_mandatory())
103 last_revnum = repository.get_latest_revnum()
104 scheme = get_property_scheme(repository, last_revnum)
105 if scheme is not None:
106 return (scheme, True)
111 def get_property_scheme(repository, revnum=None):
113 revnum = repository.get_latest_revnum()
114 text = repository.branchprop_list.get_properties("", revnum).get(SVN_PROP_BZR_BRANCHING_SCHEME, None)
117 return ListBranchingScheme(parse_list_scheme_text(text))
120 def set_property_scheme(repository, scheme):
121 def done(revmetadata, pool):
123 editor = repository.transport.get_commit_editor(
124 {properties.PROP_REVISION_LOG: "Updating branching scheme for Bazaar."},
126 root = editor.open_root(-1)
127 editor.change_dir_prop(root, SVN_PROP_BZR_BRANCHING_SCHEME,
128 "".join(map(lambda x: x+"\n", scheme.branch_list)).encode("utf-8"))
129 editor.close_directory(root)
133 def repository_guess_scheme(repository, last_revnum, branch_path=None):
134 pb = ui.ui_factory.nested_progress_bar()
136 scheme = guess_scheme_from_history(
137 repository._log.iter_changes(None, last_revnum, max(0, last_revnum-SCHEME_GUESS_SAMPLE_SIZE), pb=pb), last_revnum, branch_path)
140 mutter("Guessed branching scheme: %r" % scheme)
144 def config_set_scheme(repository, scheme, mandatory=False):
145 repository.get_config().set_branching_scheme(str(scheme),
148 def set_branching_scheme(repository, scheme, mandatory=False):
149 repository.get_mapping().scheme = scheme
150 config_set_scheme(repository, scheme, mandatory)
153 class BzrSvnMappingv3(mapping.BzrSvnMapping):
154 """The third version of the mappings as used in the 0.4.x series.
158 upgrade_suffix = "-svn3"
159 revid_prefix = "svn-v3-"
161 def __init__(self, scheme):
162 mapping.BzrSvnMapping.__init__(self)
164 assert not isinstance(scheme, str)
166 def get_mandated_layout(self, repository):
167 return SchemeDerivedLayout(repository, self.scheme)
170 def from_repository(cls, repository, _hinted_branch_path=None):
171 (scheme, mandatory) = get_stored_scheme(repository)
175 if scheme is not None:
176 if (_hinted_branch_path is None or
177 scheme.is_branch(_hinted_branch_path)):
180 last_revnum = repository.get_latest_revnum()
181 scheme = repository_guess_scheme(repository, last_revnum, _hinted_branch_path)
183 config_set_scheme(repository, scheme, mandatory=False)
188 return "%s(%r)" % (self.__class__.__name__, self.scheme)
190 def generate_file_id(self, uuid, revnum, branch, inv_path):
191 assert isinstance(uuid, str)
192 assert isinstance(revnum, int)
193 assert isinstance(branch, str)
194 assert isinstance(inv_path, unicode)
195 inv_path = inv_path.encode("utf-8")
196 ret = "%d@%s:%s:%s" % (revnum, uuid, mapping.escape_svn_path(branch),
197 mapping.escape_svn_path(inv_path))
199 ret = "%d@%s:%s;%s" % (revnum, uuid,
200 mapping.escape_svn_path(branch),
201 sha.new(inv_path).hexdigest())
202 assert isinstance(ret, str)
203 return osutils.safe_file_id(ret)
206 def supports_roundtripping():
210 def _parse_revision_id(cls, revid):
211 assert isinstance(revid, str)
213 if not revid.startswith(cls.revid_prefix):
214 raise InvalidRevisionId(revid, "")
217 (version, uuid, branch_path, srevnum) = revid.split(":")
219 raise InvalidRevisionId(revid, "")
221 scheme = version[len(cls.revid_prefix):]
223 branch_path = mapping.unescape_svn_path(branch_path)
225 return (uuid, branch_path, int(srevnum), scheme)
228 def parse_revision_id(cls, revid):
229 (uuid, branch_path, srevnum, scheme) = cls._parse_revision_id(revid)
230 # Some older versions of bzr-svn 0.4 did not always set a branching
231 # scheme but set "undefined" instead.
232 if scheme == "undefined":
233 scheme = guess_scheme_from_branch_path(branch_path)
235 scheme = BranchingScheme.find_scheme(scheme)
237 return (uuid, branch_path, srevnum, cls(scheme))
239 def is_branch(self, branch_path):
240 return (self.scheme.is_branch(branch_path) or
241 self.scheme.is_tag(branch_path))
243 def is_tag(self, tag_path):
244 return self.scheme.is_tag(tag_path)
247 def _generate_revision_id(cls, uuid, revnum, path, scheme):
248 assert isinstance(revnum, int)
249 assert isinstance(path, str)
251 assert revnum > 0 or path == "", \
252 "Trying to generate revid for (%r,%r)" % (path, revnum)
253 return "%s%s:%s:%s:%d" % (cls.revid_prefix, scheme, uuid, \
254 mapping.escape_svn_path(path.strip("/")), revnum)
256 def generate_revision_id(self, uuid, revnum, path):
257 return self._generate_revision_id(uuid, revnum, path, self.scheme)
259 def unprefix(self, branch_path, repos_path):
260 (bp, np) = self.scheme.unprefix(repos_path)
261 assert branch_path == bp
264 def __eq__(self, other):
265 return type(self) == type(other) and self.scheme == other.scheme
267 class BzrSvnMappingv3FileProps(mapping.BzrSvnMappingFileProps, BzrSvnMappingv3):
271 class BzrSvnMappingv3RevProps(mapping.BzrSvnMappingRevProps, BzrSvnMappingv3):
275 class BzrSvnMappingv3Hybrid(BzrSvnMappingv3):
276 def __init__(self, scheme):
277 BzrSvnMappingv3.__init__(self, scheme)
278 self.revprops = BzrSvnMappingv3RevProps(scheme)
279 self.fileprops = BzrSvnMappingv3FileProps(scheme)
281 def get_rhs_parents(self, branch_path, svn_revprops, fileprops):
282 if svn_revprops.has_key(mapping.SVN_REVPROP_BZR_MAPPING_VERSION):
283 return self.revprops.get_rhs_parents(branch_path, svn_revprops, fileprops)
285 return self.fileprops.get_rhs_parents(branch_path, svn_revprops, fileprops)
287 def get_revision_id(self, branch_path, revprops, fileprops):
288 if revprops.has_key(mapping.SVN_REVPROP_BZR_MAPPING_VERSION):
289 return self.revprops.get_revision_id(branch_path, revprops, fileprops)
291 return self.fileprops.get_revision_id(branch_path, revprops, fileprops)
293 def import_fileid_map(self, svn_revprops, fileprops):
294 if svn_revprops.has_key(mapping.SVN_REVPROP_BZR_MAPPING_VERSION):
295 return self.revprops.import_fileid_map(svn_revprops, fileprops)
297 return self.fileprops.import_fileid_map(svn_revprops, fileprops)
299 def export_revision(self, branch_root, timestamp, timezone, committer, revprops, revision_id,
300 revno, merges, fileprops):
301 (_, fileprops) = self.fileprops.export_revision(branch_root, timestamp, timezone, committer,
302 revprops, revision_id, revno, merges, fileprops)
303 (revprops, _) = self.revprops.export_revision(branch_root, timestamp, timezone, committer,
304 revprops, revision_id, revno, merges, fileprops)
305 return (revprops, fileprops)
307 def export_fileid_map(self, fileids, revprops, fileprops):
308 self.fileprops.export_fileid_map(fileids, revprops, fileprops)
309 self.revprops.export_fileid_map(fileids, revprops, fileprops)
311 def import_revision(self, svn_revprops, fileprops, rev):
312 self.fileprops.import_revision(svn_revprops, fileprops, rev)
313 self.revprops.import_revision(svn_revprops, fileprops, rev)