comparison mercurial/match.py @ 14675:cfc89398f710

match: introduce basic fileset support
author Matt Mackall <mpm@selenic.com>
date Sat, 18 Jun 2011 16:53:44 -0500
parents 1c151b963254
children b6dc362b051c
comparison
equal deleted inserted replaced
14674:1c151b963254 14675:cfc89398f710
4 # 4 #
5 # This software may be used and distributed according to the terms of the 5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version. 6 # GNU General Public License version 2 or any later version.
7 7
8 import re 8 import re
9 import scmutil, util 9 import scmutil, util, fileset
10 from i18n import _ 10 from i18n import _
11
12 def _expandsets(pats, ctx):
13 '''convert set: patterns into a list of files in the given context'''
14 fset = set()
15 other = []
16
17 for kind, expr in pats:
18 if kind == 'set':
19 if not ctx:
20 raise util.Abort("fileset expression with no context")
21 s = fileset.getfileset(ctx, expr)
22 fset.update(s)
23 continue
24 other.append((kind, expr))
25 return fset, other
11 26
12 class match(object): 27 class match(object):
13 def __init__(self, root, cwd, patterns, include=[], exclude=[], 28 def __init__(self, root, cwd, patterns, include=[], exclude=[],
14 default='glob', exact=False, auditor=None, ctx=None): 29 default='glob', exact=False, auditor=None, ctx=None):
15 """build an object to match a set of file patterns 30 """build an object to match a set of file patterns
28 're:<regexp>' - a regular expression 43 're:<regexp>' - a regular expression
29 'path:<path>' - a path relative to canonroot 44 'path:<path>' - a path relative to canonroot
30 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs) 45 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
31 'relpath:<path>' - a path relative to cwd 46 'relpath:<path>' - a path relative to cwd
32 'relre:<regexp>' - a regexp that needn't match the start of a name 47 'relre:<regexp>' - a regexp that needn't match the start of a name
48 'set:<fileset>' - a fileset expression
33 '<something>' - a pattern of the specified default type 49 '<something>' - a pattern of the specified default type
34 """ 50 """
35 51
52 self._ctx = None
36 self._root = root 53 self._root = root
37 self._cwd = cwd 54 self._cwd = cwd
38 self._files = [] 55 self._files = []
39 self._anypats = bool(include or exclude) 56 self._anypats = bool(include or exclude)
40 self._ctx = ctx 57 self._ctx = ctx
41 58
42 if include: 59 if include:
43 pats = _normalize(include, 'glob', root, cwd, auditor) 60 pats = _normalize(include, 'glob', root, cwd, auditor)
44 self.includepat, im = _buildmatch(pats, '(?:/|$)') 61 self.includepat, im = _buildmatch(ctx, pats, '(?:/|$)')
45 if exclude: 62 if exclude:
46 pats = _normalize(exclude, 'glob', root, cwd, auditor) 63 pats = _normalize(exclude, 'glob', root, cwd, auditor)
47 self.excludepat, em = _buildmatch(pats, '(?:/|$)') 64 self.excludepat, em = _buildmatch(ctx, pats, '(?:/|$)')
48 if exact: 65 if exact:
49 self._files = patterns 66 self._files = patterns
50 pm = self.exact 67 pm = self.exact
51 elif patterns: 68 elif patterns:
52 pats = _normalize(patterns, default, root, cwd, auditor) 69 pats = _normalize(patterns, default, root, cwd, auditor)
53 self._files = _roots(pats) 70 self._files = _roots(pats)
54 self._anypats = self._anypats or _anypats(pats) 71 self._anypats = self._anypats or _anypats(pats)
55 self.patternspat, pm = _buildmatch(pats, '$') 72 self.patternspat, pm = _buildmatch(ctx, pats, '$')
56 73
57 if patterns or exact: 74 if patterns or exact:
58 if include: 75 if include:
59 if exclude: 76 if exclude:
60 m = lambda f: im(f) and not em(f) and pm(f) 77 m = lambda f: im(f) and not em(f) and pm(f)
161 """Split a string into an optional pattern kind prefix and the 178 """Split a string into an optional pattern kind prefix and the
162 actual pattern.""" 179 actual pattern."""
163 if ':' in pat: 180 if ':' in pat:
164 kind, val = pat.split(':', 1) 181 kind, val = pat.split(':', 1)
165 if kind in ('re', 'glob', 'path', 'relglob', 'relpath', 'relre', 182 if kind in ('re', 'glob', 'path', 'relglob', 'relpath', 'relre',
166 'listfile', 'listfile0'): 183 'listfile', 'listfile0', 'set'):
167 return kind, val 184 return kind, val
168 return default, pat 185 return default, pat
169 186
170 def _globre(pat): 187 def _globre(pat):
171 "convert a glob pattern into a regexp" 188 "convert a glob pattern into a regexp"
239 if name.startswith('^'): 256 if name.startswith('^'):
240 return name 257 return name
241 return '.*' + name 258 return '.*' + name
242 return _globre(name) + tail 259 return _globre(name) + tail
243 260
244 def _buildmatch(pats, tail): 261 def _buildmatch(ctx, pats, tail):
262 fset, pats = _expandsets(pats, ctx)
263 if not pats:
264 return "", fset.__contains__
265
266 pat, mf = _buildregexmatch(pats, tail)
267 if fset:
268 return pat, lambda f: f in fset or mf(f)
269 return pat, mf
270
271 def _buildregexmatch(pats, tail):
245 """build a matching function from a set of patterns""" 272 """build a matching function from a set of patterns"""
246 try: 273 try:
247 pat = '(?:%s)' % '|'.join([_regex(k, p, tail) for (k, p) in pats]) 274 pat = '(?:%s)' % '|'.join([_regex(k, p, tail) for (k, p) in pats])
248 if len(pat) > 20000: 275 if len(pat) > 20000:
249 raise OverflowError() 276 raise OverflowError()