##// END OF EJS Templates
match: remove last traces of unused .missing callback
Mads Kiilerich -
r21113:00cae8a2 default
parent child Browse files
Show More
@@ -1,411 +1,408
1 # match.py - filename matching
1 # match.py - filename matching
2 #
2 #
3 # Copyright 2008, 2009 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2008, 2009 Matt Mackall <mpm@selenic.com> and others
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 util, pathutil
9 import util, pathutil
10 from i18n import _
10 from i18n import _
11
11
12 def _rematcher(regex):
12 def _rematcher(regex):
13 '''compile the regexp with the best available regexp engine and return a
13 '''compile the regexp with the best available regexp engine and return a
14 matcher function'''
14 matcher function'''
15 m = util.compilere(regex)
15 m = util.compilere(regex)
16 try:
16 try:
17 # slightly faster, provided by facebook's re2 bindings
17 # slightly faster, provided by facebook's re2 bindings
18 return m.test_match
18 return m.test_match
19 except AttributeError:
19 except AttributeError:
20 return m.match
20 return m.match
21
21
22 def _expandsets(kindpats, ctx):
22 def _expandsets(kindpats, ctx):
23 '''Returns the kindpats list with the 'set' patterns expanded.'''
23 '''Returns the kindpats list with the 'set' patterns expanded.'''
24 fset = set()
24 fset = set()
25 other = []
25 other = []
26
26
27 for kind, pat in kindpats:
27 for kind, pat in kindpats:
28 if kind == 'set':
28 if kind == 'set':
29 if not ctx:
29 if not ctx:
30 raise util.Abort("fileset expression with no context")
30 raise util.Abort("fileset expression with no context")
31 s = ctx.getfileset(pat)
31 s = ctx.getfileset(pat)
32 fset.update(s)
32 fset.update(s)
33 continue
33 continue
34 other.append((kind, pat))
34 other.append((kind, pat))
35 return fset, other
35 return fset, other
36
36
37 class match(object):
37 class match(object):
38 def __init__(self, root, cwd, patterns, include=[], exclude=[],
38 def __init__(self, root, cwd, patterns, include=[], exclude=[],
39 default='glob', exact=False, auditor=None, ctx=None):
39 default='glob', exact=False, auditor=None, ctx=None):
40 """build an object to match a set of file patterns
40 """build an object to match a set of file patterns
41
41
42 arguments:
42 arguments:
43 root - the canonical root of the tree you're matching against
43 root - the canonical root of the tree you're matching against
44 cwd - the current working directory, if relevant
44 cwd - the current working directory, if relevant
45 patterns - patterns to find
45 patterns - patterns to find
46 include - patterns to include (unless they are excluded)
46 include - patterns to include (unless they are excluded)
47 exclude - patterns to exclude (even if they are included)
47 exclude - patterns to exclude (even if they are included)
48 default - if a pattern in patterns has no explicit type, assume this one
48 default - if a pattern in patterns has no explicit type, assume this one
49 exact - patterns are actually filenames (include/exclude still apply)
49 exact - patterns are actually filenames (include/exclude still apply)
50
50
51 a pattern is one of:
51 a pattern is one of:
52 'glob:<glob>' - a glob relative to cwd
52 'glob:<glob>' - a glob relative to cwd
53 're:<regexp>' - a regular expression
53 're:<regexp>' - a regular expression
54 'path:<path>' - a path relative to repository root
54 'path:<path>' - a path relative to repository root
55 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
55 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
56 'relpath:<path>' - a path relative to cwd
56 'relpath:<path>' - a path relative to cwd
57 'relre:<regexp>' - a regexp that needn't match the start of a name
57 'relre:<regexp>' - a regexp that needn't match the start of a name
58 'set:<fileset>' - a fileset expression
58 'set:<fileset>' - a fileset expression
59 '<something>' - a pattern of the specified default type
59 '<something>' - a pattern of the specified default type
60 """
60 """
61
61
62 self._root = root
62 self._root = root
63 self._cwd = cwd
63 self._cwd = cwd
64 self._files = [] # exact files and roots of patterns
64 self._files = [] # exact files and roots of patterns
65 self._anypats = bool(include or exclude)
65 self._anypats = bool(include or exclude)
66 self._ctx = ctx
66 self._ctx = ctx
67 self._always = False
67 self._always = False
68
68
69 if include:
69 if include:
70 kindpats = _normalize(include, 'glob', root, cwd, auditor)
70 kindpats = _normalize(include, 'glob', root, cwd, auditor)
71 self.includepat, im = _buildmatch(ctx, kindpats, '(?:/|$)')
71 self.includepat, im = _buildmatch(ctx, kindpats, '(?:/|$)')
72 if exclude:
72 if exclude:
73 kindpats = _normalize(exclude, 'glob', root, cwd, auditor)
73 kindpats = _normalize(exclude, 'glob', root, cwd, auditor)
74 self.excludepat, em = _buildmatch(ctx, kindpats, '(?:/|$)')
74 self.excludepat, em = _buildmatch(ctx, kindpats, '(?:/|$)')
75 if exact:
75 if exact:
76 if isinstance(patterns, list):
76 if isinstance(patterns, list):
77 self._files = patterns
77 self._files = patterns
78 else:
78 else:
79 self._files = list(patterns)
79 self._files = list(patterns)
80 pm = self.exact
80 pm = self.exact
81 elif patterns:
81 elif patterns:
82 kindpats = _normalize(patterns, default, root, cwd, auditor)
82 kindpats = _normalize(patterns, default, root, cwd, auditor)
83 self._files = _roots(kindpats)
83 self._files = _roots(kindpats)
84 self._anypats = self._anypats or _anypats(kindpats)
84 self._anypats = self._anypats or _anypats(kindpats)
85 self.patternspat, pm = _buildmatch(ctx, kindpats, '$')
85 self.patternspat, pm = _buildmatch(ctx, kindpats, '$')
86
86
87 if patterns or exact:
87 if patterns or exact:
88 if include:
88 if include:
89 if exclude:
89 if exclude:
90 m = lambda f: im(f) and not em(f) and pm(f)
90 m = lambda f: im(f) and not em(f) and pm(f)
91 else:
91 else:
92 m = lambda f: im(f) and pm(f)
92 m = lambda f: im(f) and pm(f)
93 else:
93 else:
94 if exclude:
94 if exclude:
95 m = lambda f: not em(f) and pm(f)
95 m = lambda f: not em(f) and pm(f)
96 else:
96 else:
97 m = pm
97 m = pm
98 else:
98 else:
99 if include:
99 if include:
100 if exclude:
100 if exclude:
101 m = lambda f: im(f) and not em(f)
101 m = lambda f: im(f) and not em(f)
102 else:
102 else:
103 m = im
103 m = im
104 else:
104 else:
105 if exclude:
105 if exclude:
106 m = lambda f: not em(f)
106 m = lambda f: not em(f)
107 else:
107 else:
108 m = lambda f: True
108 m = lambda f: True
109 self._always = True
109 self._always = True
110
110
111 self.matchfn = m
111 self.matchfn = m
112 self._fmap = set(self._files)
112 self._fmap = set(self._files)
113
113
114 def __call__(self, fn):
114 def __call__(self, fn):
115 return self.matchfn(fn)
115 return self.matchfn(fn)
116 def __iter__(self):
116 def __iter__(self):
117 for f in self._files:
117 for f in self._files:
118 yield f
118 yield f
119
119
120 # Callbacks related to how the matcher is used by dirstate.walk.
120 # Callbacks related to how the matcher is used by dirstate.walk.
121 # Subscribers to these events must monkeypatch the matcher object.
121 # Subscribers to these events must monkeypatch the matcher object.
122 def bad(self, f, msg):
122 def bad(self, f, msg):
123 '''Callback from dirstate.walk for each explicit file that can't be
123 '''Callback from dirstate.walk for each explicit file that can't be
124 found/accessed, with an error message.'''
124 found/accessed, with an error message.'''
125 pass
125 pass
126
126
127 # If an explicitdir is set, it will be called when an explicitly listed
127 # If an explicitdir is set, it will be called when an explicitly listed
128 # directory is visited.
128 # directory is visited.
129 explicitdir = None
129 explicitdir = None
130
130
131 # If an traversedir is set, it will be called when a directory discovered
131 # If an traversedir is set, it will be called when a directory discovered
132 # by recursive traversal is visited.
132 # by recursive traversal is visited.
133 traversedir = None
133 traversedir = None
134
134
135 def missing(self, f):
136 pass
137
138 def rel(self, f):
135 def rel(self, f):
139 '''Convert repo path back to path that is relative to cwd of matcher.'''
136 '''Convert repo path back to path that is relative to cwd of matcher.'''
140 return util.pathto(self._root, self._cwd, f)
137 return util.pathto(self._root, self._cwd, f)
141
138
142 def files(self):
139 def files(self):
143 '''Explicitly listed files or patterns or roots:
140 '''Explicitly listed files or patterns or roots:
144 if no patterns or .always(): empty list,
141 if no patterns or .always(): empty list,
145 if exact: list exact files,
142 if exact: list exact files,
146 if not .anypats(): list all files and dirs,
143 if not .anypats(): list all files and dirs,
147 else: optimal roots'''
144 else: optimal roots'''
148 return self._files
145 return self._files
149
146
150 def exact(self, f):
147 def exact(self, f):
151 '''Returns True if f is in .files().'''
148 '''Returns True if f is in .files().'''
152 return f in self._fmap
149 return f in self._fmap
153
150
154 def anypats(self):
151 def anypats(self):
155 '''Matcher uses patterns or include/exclude.'''
152 '''Matcher uses patterns or include/exclude.'''
156 return self._anypats
153 return self._anypats
157
154
158 def always(self):
155 def always(self):
159 '''Matcher will match everything and .files() will be empty
156 '''Matcher will match everything and .files() will be empty
160 - optimization might be possible and necessary.'''
157 - optimization might be possible and necessary.'''
161 return self._always
158 return self._always
162
159
163 class exact(match):
160 class exact(match):
164 def __init__(self, root, cwd, files):
161 def __init__(self, root, cwd, files):
165 match.__init__(self, root, cwd, files, exact=True)
162 match.__init__(self, root, cwd, files, exact=True)
166
163
167 class always(match):
164 class always(match):
168 def __init__(self, root, cwd):
165 def __init__(self, root, cwd):
169 match.__init__(self, root, cwd, [])
166 match.__init__(self, root, cwd, [])
170 self._always = True
167 self._always = True
171
168
172 class narrowmatcher(match):
169 class narrowmatcher(match):
173 """Adapt a matcher to work on a subdirectory only.
170 """Adapt a matcher to work on a subdirectory only.
174
171
175 The paths are remapped to remove/insert the path as needed:
172 The paths are remapped to remove/insert the path as needed:
176
173
177 >>> m1 = match('root', '', ['a.txt', 'sub/b.txt'])
174 >>> m1 = match('root', '', ['a.txt', 'sub/b.txt'])
178 >>> m2 = narrowmatcher('sub', m1)
175 >>> m2 = narrowmatcher('sub', m1)
179 >>> bool(m2('a.txt'))
176 >>> bool(m2('a.txt'))
180 False
177 False
181 >>> bool(m2('b.txt'))
178 >>> bool(m2('b.txt'))
182 True
179 True
183 >>> bool(m2.matchfn('a.txt'))
180 >>> bool(m2.matchfn('a.txt'))
184 False
181 False
185 >>> bool(m2.matchfn('b.txt'))
182 >>> bool(m2.matchfn('b.txt'))
186 True
183 True
187 >>> m2.files()
184 >>> m2.files()
188 ['b.txt']
185 ['b.txt']
189 >>> m2.exact('b.txt')
186 >>> m2.exact('b.txt')
190 True
187 True
191 >>> m2.rel('b.txt')
188 >>> m2.rel('b.txt')
192 'b.txt'
189 'b.txt'
193 >>> def bad(f, msg):
190 >>> def bad(f, msg):
194 ... print "%s: %s" % (f, msg)
191 ... print "%s: %s" % (f, msg)
195 >>> m1.bad = bad
192 >>> m1.bad = bad
196 >>> m2.bad('x.txt', 'No such file')
193 >>> m2.bad('x.txt', 'No such file')
197 sub/x.txt: No such file
194 sub/x.txt: No such file
198 """
195 """
199
196
200 def __init__(self, path, matcher):
197 def __init__(self, path, matcher):
201 self._root = matcher._root
198 self._root = matcher._root
202 self._cwd = matcher._cwd
199 self._cwd = matcher._cwd
203 self._path = path
200 self._path = path
204 self._matcher = matcher
201 self._matcher = matcher
205 self._always = matcher._always
202 self._always = matcher._always
206
203
207 self._files = [f[len(path) + 1:] for f in matcher._files
204 self._files = [f[len(path) + 1:] for f in matcher._files
208 if f.startswith(path + "/")]
205 if f.startswith(path + "/")]
209 self._anypats = matcher._anypats
206 self._anypats = matcher._anypats
210 self.matchfn = lambda fn: matcher.matchfn(self._path + "/" + fn)
207 self.matchfn = lambda fn: matcher.matchfn(self._path + "/" + fn)
211 self._fmap = set(self._files)
208 self._fmap = set(self._files)
212
209
213 def bad(self, f, msg):
210 def bad(self, f, msg):
214 self._matcher.bad(self._path + "/" + f, msg)
211 self._matcher.bad(self._path + "/" + f, msg)
215
212
216 def patkind(pattern, default=None):
213 def patkind(pattern, default=None):
217 '''If pattern is 'kind:pat' with a known kind, return kind.'''
214 '''If pattern is 'kind:pat' with a known kind, return kind.'''
218 return _patsplit(pattern, default)[0]
215 return _patsplit(pattern, default)[0]
219
216
220 def _patsplit(pattern, default):
217 def _patsplit(pattern, default):
221 """Split a string into the optional pattern kind prefix and the actual
218 """Split a string into the optional pattern kind prefix and the actual
222 pattern."""
219 pattern."""
223 if ':' in pattern:
220 if ':' in pattern:
224 kind, pat = pattern.split(':', 1)
221 kind, pat = pattern.split(':', 1)
225 if kind in ('re', 'glob', 'path', 'relglob', 'relpath', 'relre',
222 if kind in ('re', 'glob', 'path', 'relglob', 'relpath', 'relre',
226 'listfile', 'listfile0', 'set'):
223 'listfile', 'listfile0', 'set'):
227 return kind, pat
224 return kind, pat
228 return default, pattern
225 return default, pattern
229
226
230 def _globre(pat):
227 def _globre(pat):
231 r'''Convert an extended glob string to a regexp string.
228 r'''Convert an extended glob string to a regexp string.
232
229
233 >>> print _globre(r'?')
230 >>> print _globre(r'?')
234 .
231 .
235 >>> print _globre(r'*')
232 >>> print _globre(r'*')
236 [^/]*
233 [^/]*
237 >>> print _globre(r'**')
234 >>> print _globre(r'**')
238 .*
235 .*
239 >>> print _globre(r'[a*?!^][^b][!c]')
236 >>> print _globre(r'[a*?!^][^b][!c]')
240 [a*?!^][\^b][^c]
237 [a*?!^][\^b][^c]
241 >>> print _globre(r'{a,b}')
238 >>> print _globre(r'{a,b}')
242 (?:a|b)
239 (?:a|b)
243 >>> print _globre(r'.\*\?')
240 >>> print _globre(r'.\*\?')
244 \.\*\?
241 \.\*\?
245 '''
242 '''
246 i, n = 0, len(pat)
243 i, n = 0, len(pat)
247 res = ''
244 res = ''
248 group = 0
245 group = 0
249 escape = re.escape
246 escape = re.escape
250 def peek():
247 def peek():
251 return i < n and pat[i]
248 return i < n and pat[i]
252 while i < n:
249 while i < n:
253 c = pat[i]
250 c = pat[i]
254 i += 1
251 i += 1
255 if c not in '*?[{},\\':
252 if c not in '*?[{},\\':
256 res += escape(c)
253 res += escape(c)
257 elif c == '*':
254 elif c == '*':
258 if peek() == '*':
255 if peek() == '*':
259 i += 1
256 i += 1
260 res += '.*'
257 res += '.*'
261 else:
258 else:
262 res += '[^/]*'
259 res += '[^/]*'
263 elif c == '?':
260 elif c == '?':
264 res += '.'
261 res += '.'
265 elif c == '[':
262 elif c == '[':
266 j = i
263 j = i
267 if j < n and pat[j] in '!]':
264 if j < n and pat[j] in '!]':
268 j += 1
265 j += 1
269 while j < n and pat[j] != ']':
266 while j < n and pat[j] != ']':
270 j += 1
267 j += 1
271 if j >= n:
268 if j >= n:
272 res += '\\['
269 res += '\\['
273 else:
270 else:
274 stuff = pat[i:j].replace('\\','\\\\')
271 stuff = pat[i:j].replace('\\','\\\\')
275 i = j + 1
272 i = j + 1
276 if stuff[0] == '!':
273 if stuff[0] == '!':
277 stuff = '^' + stuff[1:]
274 stuff = '^' + stuff[1:]
278 elif stuff[0] == '^':
275 elif stuff[0] == '^':
279 stuff = '\\' + stuff
276 stuff = '\\' + stuff
280 res = '%s[%s]' % (res, stuff)
277 res = '%s[%s]' % (res, stuff)
281 elif c == '{':
278 elif c == '{':
282 group += 1
279 group += 1
283 res += '(?:'
280 res += '(?:'
284 elif c == '}' and group:
281 elif c == '}' and group:
285 res += ')'
282 res += ')'
286 group -= 1
283 group -= 1
287 elif c == ',' and group:
284 elif c == ',' and group:
288 res += '|'
285 res += '|'
289 elif c == '\\':
286 elif c == '\\':
290 p = peek()
287 p = peek()
291 if p:
288 if p:
292 i += 1
289 i += 1
293 res += escape(p)
290 res += escape(p)
294 else:
291 else:
295 res += escape(c)
292 res += escape(c)
296 else:
293 else:
297 res += escape(c)
294 res += escape(c)
298 return res
295 return res
299
296
300 def _regex(kind, pat, globsuffix):
297 def _regex(kind, pat, globsuffix):
301 '''Convert a (normalized) pattern of any kind into a regular expression.
298 '''Convert a (normalized) pattern of any kind into a regular expression.
302 globsuffix is appended to the regexp of globs.'''
299 globsuffix is appended to the regexp of globs.'''
303 if not pat:
300 if not pat:
304 return ''
301 return ''
305 if kind == 're':
302 if kind == 're':
306 return pat
303 return pat
307 if kind == 'path':
304 if kind == 'path':
308 return '^' + re.escape(pat) + '(?:/|$)'
305 return '^' + re.escape(pat) + '(?:/|$)'
309 if kind == 'relglob':
306 if kind == 'relglob':
310 return '(?:|.*/)' + _globre(pat) + globsuffix
307 return '(?:|.*/)' + _globre(pat) + globsuffix
311 if kind == 'relpath':
308 if kind == 'relpath':
312 return re.escape(pat) + '(?:/|$)'
309 return re.escape(pat) + '(?:/|$)'
313 if kind == 'relre':
310 if kind == 'relre':
314 if pat.startswith('^'):
311 if pat.startswith('^'):
315 return pat
312 return pat
316 return '.*' + pat
313 return '.*' + pat
317 return _globre(pat) + globsuffix
314 return _globre(pat) + globsuffix
318
315
319 def _buildmatch(ctx, kindpats, globsuffix):
316 def _buildmatch(ctx, kindpats, globsuffix):
320 '''Return regexp string and a matcher function for kindpats.
317 '''Return regexp string and a matcher function for kindpats.
321 globsuffix is appended to the regexp of globs.'''
318 globsuffix is appended to the regexp of globs.'''
322 fset, kindpats = _expandsets(kindpats, ctx)
319 fset, kindpats = _expandsets(kindpats, ctx)
323 if not kindpats:
320 if not kindpats:
324 return "", fset.__contains__
321 return "", fset.__contains__
325
322
326 regex, mf = _buildregexmatch(kindpats, globsuffix)
323 regex, mf = _buildregexmatch(kindpats, globsuffix)
327 if fset:
324 if fset:
328 return regex, lambda f: f in fset or mf(f)
325 return regex, lambda f: f in fset or mf(f)
329 return regex, mf
326 return regex, mf
330
327
331 def _buildregexmatch(kindpats, globsuffix):
328 def _buildregexmatch(kindpats, globsuffix):
332 """Build a match function from a list of kinds and kindpats,
329 """Build a match function from a list of kinds and kindpats,
333 return regexp string and a matcher function."""
330 return regexp string and a matcher function."""
334 try:
331 try:
335 regex = '(?:%s)' % '|'.join([_regex(k, p, globsuffix)
332 regex = '(?:%s)' % '|'.join([_regex(k, p, globsuffix)
336 for (k, p) in kindpats])
333 for (k, p) in kindpats])
337 if len(regex) > 20000:
334 if len(regex) > 20000:
338 raise OverflowError
335 raise OverflowError
339 return regex, _rematcher(regex)
336 return regex, _rematcher(regex)
340 except OverflowError:
337 except OverflowError:
341 # We're using a Python with a tiny regex engine and we
338 # We're using a Python with a tiny regex engine and we
342 # made it explode, so we'll divide the pattern list in two
339 # made it explode, so we'll divide the pattern list in two
343 # until it works
340 # until it works
344 l = len(kindpats)
341 l = len(kindpats)
345 if l < 2:
342 if l < 2:
346 raise
343 raise
347 regexa, a = _buildregexmatch(kindpats[:l//2], globsuffix)
344 regexa, a = _buildregexmatch(kindpats[:l//2], globsuffix)
348 regexb, b = _buildregexmatch(kindpats[l//2:], globsuffix)
345 regexb, b = _buildregexmatch(kindpats[l//2:], globsuffix)
349 return pat, lambda s: a(s) or b(s)
346 return pat, lambda s: a(s) or b(s)
350 except re.error:
347 except re.error:
351 for k, p in kindpats:
348 for k, p in kindpats:
352 try:
349 try:
353 _rematcher('(?:%s)' % _regex(k, p, globsuffix))
350 _rematcher('(?:%s)' % _regex(k, p, globsuffix))
354 except re.error:
351 except re.error:
355 raise util.Abort(_("invalid pattern (%s): %s") % (k, p))
352 raise util.Abort(_("invalid pattern (%s): %s") % (k, p))
356 raise util.Abort(_("invalid pattern"))
353 raise util.Abort(_("invalid pattern"))
357
354
358 def _normalize(patterns, default, root, cwd, auditor):
355 def _normalize(patterns, default, root, cwd, auditor):
359 '''Convert 'kind:pat' from the patterns list to tuples with kind and
356 '''Convert 'kind:pat' from the patterns list to tuples with kind and
360 normalized and rooted patterns and with listfiles expanded.'''
357 normalized and rooted patterns and with listfiles expanded.'''
361 kindpats = []
358 kindpats = []
362 for kind, pat in [_patsplit(p, default) for p in patterns]:
359 for kind, pat in [_patsplit(p, default) for p in patterns]:
363 if kind in ('glob', 'relpath'):
360 if kind in ('glob', 'relpath'):
364 pat = pathutil.canonpath(root, cwd, pat, auditor)
361 pat = pathutil.canonpath(root, cwd, pat, auditor)
365 elif kind in ('relglob', 'path'):
362 elif kind in ('relglob', 'path'):
366 pat = util.normpath(pat)
363 pat = util.normpath(pat)
367 elif kind in ('listfile', 'listfile0'):
364 elif kind in ('listfile', 'listfile0'):
368 try:
365 try:
369 files = util.readfile(pat)
366 files = util.readfile(pat)
370 if kind == 'listfile0':
367 if kind == 'listfile0':
371 files = files.split('\0')
368 files = files.split('\0')
372 else:
369 else:
373 files = files.splitlines()
370 files = files.splitlines()
374 files = [f for f in files if f]
371 files = [f for f in files if f]
375 except EnvironmentError:
372 except EnvironmentError:
376 raise util.Abort(_("unable to read file list (%s)") % pat)
373 raise util.Abort(_("unable to read file list (%s)") % pat)
377 kindpats += _normalize(files, default, root, cwd, auditor)
374 kindpats += _normalize(files, default, root, cwd, auditor)
378 continue
375 continue
379 # else: re or relre - which cannot be normalized
376 # else: re or relre - which cannot be normalized
380 kindpats.append((kind, pat))
377 kindpats.append((kind, pat))
381 return kindpats
378 return kindpats
382
379
383 def _roots(kindpats):
380 def _roots(kindpats):
384 '''return roots and exact explicitly listed files from patterns
381 '''return roots and exact explicitly listed files from patterns
385
382
386 >>> _roots([('glob', 'g/*'), ('glob', 'g'), ('glob', 'g*')])
383 >>> _roots([('glob', 'g/*'), ('glob', 'g'), ('glob', 'g*')])
387 ['g', 'g', '.']
384 ['g', 'g', '.']
388 >>> _roots([('relpath', 'r'), ('path', 'p/p'), ('path', '')])
385 >>> _roots([('relpath', 'r'), ('path', 'p/p'), ('path', '')])
389 ['r', 'p/p', '.']
386 ['r', 'p/p', '.']
390 >>> _roots([('relglob', 'rg*'), ('re', 're/'), ('relre', 'rr')])
387 >>> _roots([('relglob', 'rg*'), ('re', 're/'), ('relre', 'rr')])
391 ['.', '.', '.']
388 ['.', '.', '.']
392 '''
389 '''
393 r = []
390 r = []
394 for kind, pat in kindpats:
391 for kind, pat in kindpats:
395 if kind == 'glob': # find the non-glob prefix
392 if kind == 'glob': # find the non-glob prefix
396 root = []
393 root = []
397 for p in pat.split('/'):
394 for p in pat.split('/'):
398 if '[' in p or '{' in p or '*' in p or '?' in p:
395 if '[' in p or '{' in p or '*' in p or '?' in p:
399 break
396 break
400 root.append(p)
397 root.append(p)
401 r.append('/'.join(root) or '.')
398 r.append('/'.join(root) or '.')
402 elif kind in ('relpath', 'path'):
399 elif kind in ('relpath', 'path'):
403 r.append(pat or '.')
400 r.append(pat or '.')
404 else: # relglob, re, relre
401 else: # relglob, re, relre
405 r.append('.')
402 r.append('.')
406 return r
403 return r
407
404
408 def _anypats(kindpats):
405 def _anypats(kindpats):
409 for kind, pat in kindpats:
406 for kind, pat in kindpats:
410 if kind in ('glob', 're', 'relglob', 'relre', 'set'):
407 if kind in ('glob', 're', 'relglob', 'relre', 'set'):
411 return True
408 return True
General Comments 0
You need to be logged in to leave comments. Login now