1
# Copyright (C) 2005-2009 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 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, see <http://www.gnu.org/licenses/>.
16
"""Repository layouts."""
19
from subvertpy.ra import DIRENT_KIND
26
from bzrlib.trace import mutter
28
from bzrlib.plugins.svn.errors import NotSvnBranchPath, NoCustomBranchPaths
30
class RepositoryLayout(object):
31
"""Describes a repository layout."""
36
def get_project_prefixes(self, project):
39
def supports_tags(self):
42
def get_tag_path(self, name, project=""):
43
"""Return the path at which the tag with specified name should be found.
45
:param name: Name of the tag.
46
:param project: Optional name of the project the tag is for. Can include slashes.
47
:return: Path of the tag.
49
raise NotImplementedError
51
def get_tag_name(self, path, project=""):
52
"""Determine the tag name from a tag path.
54
:param path: Path inside the repository.
56
raise NotImplementedError
58
def push_merged_revisions(self, project=""):
59
"""Determine whether or not right hand side (merged) revisions should be pushed.
63
:param project: Name of the project.
67
def get_branch_path(self, name, project=""):
68
"""Return the path at which the branch with specified name should be found.
70
:param name: Name of the branch.
71
:param project: Optional name of the project the branch is for. Can include slashes.
72
:return: Path of the branch.
74
raise NoCustomBranchPaths(self)
76
def parse(self, path):
79
:return: Tuple with type ('tag', 'branch'), project name, branch path and path
82
raise NotImplementedError
84
def split_project_path(self, path, project):
85
"""Parse a project inside a particular project.
88
(pt, parsed_project, bp, ip) = self.parse(path)
89
if project is not None and parsed_project != project:
90
raise NotSvnBranchPath(path, self)
93
def is_branch(self, path, project=None):
94
"""Check whether a specified path points at a branch."""
96
(type, proj, bp, rp) = self.parse(path)
97
except NotSvnBranchPath:
99
if (type == "branch" and rp == "" and
100
(project is None or proj == project)):
104
def is_tag(self, path, project=None):
105
"""Check whether a specified path points at a tag."""
107
(type, proj, bp, rp) = self.parse(path)
108
except NotSvnBranchPath:
110
if (type == "tag" and rp == "" and
111
(project is None or proj == project)):
115
def is_branch_parent(self, path, project=None):
116
return self.is_branch(urlutils.join(path, "trunk"), project)
118
def is_tag_parent(self, path, project=None):
119
return self.is_tag(urlutils.join(path, "trunk"), project)
121
def is_branch_or_tag(self, path, project=None):
122
return self.is_branch(path, project) or self.is_tag(path, project)
124
def is_branch_or_tag_parent(self, path, project=None):
125
return self.is_branch_parent(path, project) or self.is_tag_parent(path, project)
127
def get_branches(self, repository, revnum, project="", pb=None):
128
"""Retrieve a list of paths that refer to branches in a specific revision.
130
:result: Iterator over tuples with (project, branch path)
132
raise NotImplementedError(self.get_branches)
134
def get_tags(self, repository, revnum, project="", pb=None):
135
"""Retrieve a list of paths that refer to tags in a specific revision.
137
:result: Iterator over tuples with (project, branch path)
139
raise NotImplementedError(self.get_tags)
142
def wildcard_matches(path, pattern):
143
ar = path.strip("/").split("/")
144
br = pattern.strip("/").split("/")
145
if len(ar) != len(br):
147
for a, b in zip(ar, br):
148
if b != a and not (a != "" and b == "*"):
153
def expand_branch_pattern(begin, todo, check_path, get_children, project=None):
154
"""Find the paths in the repository that match the expected branch pattern.
156
:param begin: List of path elements currently opened.
157
:param todo: List of path elements to still evaluate (including wildcards)
158
:param check_path: Function for checking a path exists
159
:param get_children: Function for retrieving the children of a path
161
mutter('expand branches: %r, %r', begin, todo)
162
path = "/".join(begin)
163
if (project is not None and
164
not project.startswith(path) and
165
not path.startswith(project)):
167
# If all elements have already been handled, just check the path exists
173
# Not a wildcard? Just expand next bits
175
return expand_branch_pattern(begin+[todo[0]], todo[1:], check_path,
176
get_children, project)
177
children = get_children(path)
181
pb = ui.ui_factory.nested_progress_bar()
183
for idx, c in enumerate(children):
184
pb.update("browsing branches", idx, len(children))
186
# Last path element, so return directly
187
ret.append("/".join(begin+[c]))
189
ret += expand_branch_pattern(begin+[c], todo[1:], check_path,
190
get_children, project)
196
def get_root_paths(repository, itemlist, revnum, verify_fn, project=None, pb=None):
197
"""Find all the paths in the repository matching a list of items.
199
:param repository: Repository to search in.
200
:param itemlist: List of glob-items to match on.
201
:param revnum: Revision number in repository to analyse.
202
:param verify_fn: Function that checks if a path is acceptable.
203
:param project: Optional project branch/tag should be in.
204
:param pb: Optional progress bar.
206
def check_path(path):
207
return repository.transport.check_path(path, revnum) == subvertpy.NODE_DIR
208
def find_children(path):
210
assert not path.startswith("/")
211
dirents = repository.transport.get_dir(path, revnum, DIRENT_KIND)[0]
212
except subvertpy.SubversionException, (msg, num):
213
if num in (subvertpy.ERR_FS_NOT_DIRECTORY,
214
subvertpy.ERR_FS_NOT_FOUND,
215
subvertpy.ERR_RA_DAV_PATH_NOT_FOUND):
218
return [d for d in dirents if dirents[d]['kind'] == subvertpy.NODE_DIR]
220
for idx, pattern in enumerate(itemlist):
221
assert isinstance(pattern, str)
223
pb.update("finding branches", idx, len(itemlist))
224
for bp in expand_branch_pattern([], pattern.strip("/").split("/"), check_path,
225
find_children, project):
226
if verify_fn(bp, project):
227
yield project, bp, bp.split("/")[-1]
230
help_layout = """Subversion repository layouts.
232
Subversion is basically a versioned file system. It does not have
233
any notion of branches and what is a branch in Subversion is therefor
236
In order for Bazaar to access a Subversion repository it has to know
237
what paths to consider branches. What it will and will not consider
238
a branch or tag is defined by the repository layout.
239
When you connect to a repository for the first time, Bazaar
240
will try to determine the layout to use using some simple
241
heuristics. It is always possible to change the branching scheme it should
244
There are some conventions in use in Subversion for repository layouts.
245
The most common one is probably the trunk/branches/tags
246
layout, where the repository contains a "trunk" directory with the main
247
development branch, other branches in a "branches" directory and tags as
248
subdirectories of a "tags" directory. This layout is named
251
Another option is simply having just one branch at the root of the repository.
252
This scheme is called "root" by Bazaar.
254
The layout bzr-svn should use for a repository can be set in the
255
configuration file ~/.bazaar/subversion.conf. If you have a custom
256
repository, you can set the "branches" and "tags" variables. These variables
257
can contain asterisks. Multiple locations can be separated by a semicolon.
260
[203ae883-c723-44c9-aabd-cb56e4f81c9a]
261
branches = path/to/*/bla;path/to/trunk
263
This would consider paths path/to/foo/bla, path/to/blie/bla and path/to/trunk
264
branches, if they existed.
269
layout_registry = registry.Registry()
270
layout_registry.register_lazy("root", "bzrlib.plugins.svn.layout.standard", "RootLayout")
271
layout_registry.register_lazy("none", "bzrlib.plugins.svn.layout.standard", "RootLayout")
272
layout_registry.register_lazy("trunk", "bzrlib.plugins.svn.layout.standard", "TrunkLayout0")
273
layout_registry.register_lazy("trunk0", "bzrlib.plugins.svn.layout.standard", "TrunkLayout0")
274
layout_registry.register_lazy("trunk1", "bzrlib.plugins.svn.layout.standard", "TrunkLayout1")
275
layout_registry.register_lazy("trunk2", "bzrlib.plugins.svn.layout.standard", "TrunkLayout2")
277
layout_registry.register_lazy("itrunk1", "bzrlib.plugins.svn.layout.standard",
278
"InverseTrunkLayout1")
279
layout_registry.register_lazy("itrunk2", "bzrlib.plugins.svn.layout.standard",
280
"InverseTrunkLayout2")
281
layout_registry.register_lazy("itrunk3", "bzrlib.plugins.svn.layout.standard",
282
"InverseTrunkLayout3")
284
class RepositoryRegistry(registry.Registry):
288
return super(RepositoryRegistry, self).get(name)()
292
repository_registry = RepositoryRegistry()
293
repository_registry.register_lazy("283d02a7-25f6-0310-bc7c-ecb5cbfe19da",
294
"bzrlib.plugins.svn.layout.custom", "KDELayout")
295
repository_registry.register_lazy("13f79535-47bb-0310-9956-ffa450edef68",
296
"bzrlib.plugins.svn.layout.custom", "ApacheLayout")