~ubuntu-branches/debian/sid/bzr-svn/sid

« back to all changes in this revision

Viewing changes to layout/__init__.py

  • Committer: Bazaar Package Importer
  • Author(s): Jelmer Vernooij
  • Date: 2009-03-10 14:38:42 UTC
  • mfrom: (1.2.1 upstream) (3.1.4 jaunty)
  • Revision ID: james.westby@ubuntu.com-20090310143842-ucp9fxog1yi3la8f
Tags: 0.5.3-1
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005-2009 Jelmer Vernooij <jelmer@samba.org>
 
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, see <http://www.gnu.org/licenses/>.
 
15
 
 
16
"""Repository layouts."""
 
17
 
 
18
import subvertpy
 
19
from subvertpy.ra import DIRENT_KIND
 
20
 
 
21
from bzrlib import (
 
22
    registry,
 
23
    urlutils,
 
24
    ui,
 
25
    )
 
26
from bzrlib.trace import mutter
 
27
 
 
28
from bzrlib.plugins.svn.errors import NotSvnBranchPath, NoCustomBranchPaths
 
29
 
 
30
class RepositoryLayout(object):
 
31
    """Describes a repository layout."""
 
32
 
 
33
    def __init__(self):
 
34
        pass
 
35
 
 
36
    def get_project_prefixes(self, project):
 
37
        return [project]
 
38
 
 
39
    def supports_tags(self):
 
40
        return True
 
41
 
 
42
    def get_tag_path(self, name, project=""):
 
43
        """Return the path at which the tag with specified name should be found.
 
44
 
 
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.
 
48
        """
 
49
        raise NotImplementedError
 
50
 
 
51
    def get_tag_name(self, path, project=""):
 
52
        """Determine the tag name from a tag path.
 
53
 
 
54
        :param path: Path inside the repository.
 
55
        """
 
56
        raise NotImplementedError
 
57
 
 
58
    def push_merged_revisions(self, project=""):
 
59
        """Determine whether or not right hand side (merged) revisions should be pushed.
 
60
 
 
61
        Defaults to False.
 
62
        
 
63
        :param project: Name of the project.
 
64
        """
 
65
        return False
 
66
 
 
67
    def get_branch_path(self, name, project=""):
 
68
        """Return the path at which the branch with specified name should be found.
 
69
 
 
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.
 
73
        """
 
74
        raise NoCustomBranchPaths(self)
 
75
 
 
76
    def parse(self, path):
 
77
        """Parse a path.
 
78
 
 
79
        :return: Tuple with type ('tag', 'branch'), project name, branch path and path 
 
80
            inside the branch
 
81
        """
 
82
        raise NotImplementedError
 
83
 
 
84
    def split_project_path(self, path, project):
 
85
        """Parse a project inside a particular project.
 
86
 
 
87
        """
 
88
        (pt, parsed_project, bp, ip) = self.parse(path)
 
89
        if project is not None and parsed_project != project:
 
90
            raise NotSvnBranchPath(path, self)
 
91
        return (pt, bp, ip)
 
92
 
 
93
    def is_branch(self, path, project=None):
 
94
        """Check whether a specified path points at a branch."""
 
95
        try:
 
96
            (type, proj, bp, rp) = self.parse(path)
 
97
        except NotSvnBranchPath:
 
98
            return False
 
99
        if (type == "branch" and rp == "" and 
 
100
            (project is None or proj == project)):
 
101
            return True
 
102
        return False
 
103
 
 
104
    def is_tag(self, path, project=None):
 
105
        """Check whether a specified path points at a tag."""
 
106
        try:
 
107
            (type, proj, bp, rp) = self.parse(path)
 
108
        except NotSvnBranchPath:
 
109
            return False
 
110
        if (type == "tag" and rp == "" and
 
111
            (project is None or proj == project)):
 
112
            return True
 
113
        return False
 
114
 
 
115
    def is_branch_parent(self, path, project=None):
 
116
        return self.is_branch(urlutils.join(path, "trunk"), project)
 
117
 
 
118
    def is_tag_parent(self, path, project=None):
 
119
        return self.is_tag(urlutils.join(path, "trunk"), project)
 
120
 
 
121
    def is_branch_or_tag(self, path, project=None):
 
122
        return self.is_branch(path, project) or self.is_tag(path, project)
 
123
 
 
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)
 
126
 
 
127
    def get_branches(self, repository, revnum, project="", pb=None):
 
128
        """Retrieve a list of paths that refer to branches in a specific revision.
 
129
 
 
130
        :result: Iterator over tuples with (project, branch path)
 
131
        """
 
132
        raise NotImplementedError(self.get_branches)
 
133
 
 
134
    def get_tags(self, repository, revnum, project="", pb=None):
 
135
        """Retrieve a list of paths that refer to tags in a specific revision.
 
136
 
 
137
        :result: Iterator over tuples with (project, branch path)
 
138
        """
 
139
        raise NotImplementedError(self.get_tags)
 
140
 
 
141
 
 
142
def wildcard_matches(path, pattern):
 
143
    ar = path.strip("/").split("/")
 
144
    br = pattern.strip("/").split("/")
 
145
    if len(ar) != len(br):
 
146
        return False
 
147
    for a, b in zip(ar, br):
 
148
        if b != a and not (a != "" and b == "*"): 
 
149
            return False
 
150
    return True
 
151
 
 
152
 
 
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.
 
155
 
 
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
 
160
    """
 
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)):
 
166
        return []
 
167
    # If all elements have already been handled, just check the path exists
 
168
    if len(todo) == 0:
 
169
        if check_path(path):
 
170
            return [path]
 
171
        else:
 
172
            return []
 
173
    # Not a wildcard? Just expand next bits
 
174
    if todo[0] != "*":
 
175
        return expand_branch_pattern(begin+[todo[0]], todo[1:], check_path, 
 
176
                                     get_children, project)
 
177
    children = get_children(path)
 
178
    if children is None:
 
179
        return []
 
180
    ret = []
 
181
    pb = ui.ui_factory.nested_progress_bar()
 
182
    try:
 
183
        for idx, c in enumerate(children):
 
184
            pb.update("browsing branches", idx, len(children))
 
185
            if len(todo) == 1:
 
186
                # Last path element, so return directly
 
187
                ret.append("/".join(begin+[c]))
 
188
            else:
 
189
                ret += expand_branch_pattern(begin+[c], todo[1:], check_path, 
 
190
                                             get_children, project)
 
191
    finally:
 
192
        pb.finished()
 
193
    return ret
 
194
 
 
195
 
 
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.
 
198
 
 
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.
 
205
    """
 
206
    def check_path(path):
 
207
        return repository.transport.check_path(path, revnum) == subvertpy.NODE_DIR
 
208
    def find_children(path):
 
209
        try:
 
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):
 
216
                return None
 
217
            raise
 
218
        return [d for d in dirents if dirents[d]['kind'] == subvertpy.NODE_DIR]
 
219
 
 
220
    for idx, pattern in enumerate(itemlist):
 
221
        assert isinstance(pattern, str)
 
222
        if pb is not None:
 
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]
 
228
 
 
229
 
 
230
help_layout = """Subversion repository layouts.
 
231
 
 
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
 
234
up to the user. 
 
235
 
 
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 
 
242
use later.
 
243
 
 
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 
 
249
"trunk" in Bazaar.
 
250
 
 
251
Another option is simply having just one branch at the root of the repository. 
 
252
This scheme is called "root" by Bazaar.
 
253
 
 
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. 
 
258
For example:
 
259
 
 
260
[203ae883-c723-44c9-aabd-cb56e4f81c9a]
 
261
branches = path/to/*/bla;path/to/trunk
 
262
 
 
263
This would consider paths path/to/foo/bla, path/to/blie/bla and path/to/trunk 
 
264
branches, if they existed.
 
265
 
 
266
"""
 
267
 
 
268
 
 
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")
 
276
 
 
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")
 
283
 
 
284
class RepositoryRegistry(registry.Registry):
 
285
 
 
286
    def get(self, name):
 
287
        try:
 
288
            return super(RepositoryRegistry, self).get(name)()
 
289
        except KeyError:
 
290
            return None
 
291
 
 
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")