##// END OF EJS Templates
match: remove superfluous pass statements
Augie Fackler -
r34377:acabbc5c default
parent child Browse files
Show More
@@ -1,1034 +1,1033
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 from __future__ import absolute_import, print_function
8 from __future__ import absolute_import, print_function
9
9
10 import copy
10 import copy
11 import os
11 import os
12 import re
12 import re
13
13
14 from .i18n import _
14 from .i18n import _
15 from . import (
15 from . import (
16 error,
16 error,
17 pathutil,
17 pathutil,
18 util,
18 util,
19 )
19 )
20
20
21 allpatternkinds = ('re', 'glob', 'path', 'relglob', 'relpath', 'relre',
21 allpatternkinds = ('re', 'glob', 'path', 'relglob', 'relpath', 'relre',
22 'listfile', 'listfile0', 'set', 'include', 'subinclude',
22 'listfile', 'listfile0', 'set', 'include', 'subinclude',
23 'rootfilesin')
23 'rootfilesin')
24 cwdrelativepatternkinds = ('relpath', 'glob')
24 cwdrelativepatternkinds = ('relpath', 'glob')
25
25
26 propertycache = util.propertycache
26 propertycache = util.propertycache
27
27
28 def _rematcher(regex):
28 def _rematcher(regex):
29 '''compile the regexp with the best available regexp engine and return a
29 '''compile the regexp with the best available regexp engine and return a
30 matcher function'''
30 matcher function'''
31 m = util.re.compile(regex)
31 m = util.re.compile(regex)
32 try:
32 try:
33 # slightly faster, provided by facebook's re2 bindings
33 # slightly faster, provided by facebook's re2 bindings
34 return m.test_match
34 return m.test_match
35 except AttributeError:
35 except AttributeError:
36 return m.match
36 return m.match
37
37
38 def _expandsets(kindpats, ctx, listsubrepos):
38 def _expandsets(kindpats, ctx, listsubrepos):
39 '''Returns the kindpats list with the 'set' patterns expanded.'''
39 '''Returns the kindpats list with the 'set' patterns expanded.'''
40 fset = set()
40 fset = set()
41 other = []
41 other = []
42
42
43 for kind, pat, source in kindpats:
43 for kind, pat, source in kindpats:
44 if kind == 'set':
44 if kind == 'set':
45 if not ctx:
45 if not ctx:
46 raise error.ProgrammingError("fileset expression with no "
46 raise error.ProgrammingError("fileset expression with no "
47 "context")
47 "context")
48 s = ctx.getfileset(pat)
48 s = ctx.getfileset(pat)
49 fset.update(s)
49 fset.update(s)
50
50
51 if listsubrepos:
51 if listsubrepos:
52 for subpath in ctx.substate:
52 for subpath in ctx.substate:
53 s = ctx.sub(subpath).getfileset(pat)
53 s = ctx.sub(subpath).getfileset(pat)
54 fset.update(subpath + '/' + f for f in s)
54 fset.update(subpath + '/' + f for f in s)
55
55
56 continue
56 continue
57 other.append((kind, pat, source))
57 other.append((kind, pat, source))
58 return fset, other
58 return fset, other
59
59
60 def _expandsubinclude(kindpats, root):
60 def _expandsubinclude(kindpats, root):
61 '''Returns the list of subinclude matcher args and the kindpats without the
61 '''Returns the list of subinclude matcher args and the kindpats without the
62 subincludes in it.'''
62 subincludes in it.'''
63 relmatchers = []
63 relmatchers = []
64 other = []
64 other = []
65
65
66 for kind, pat, source in kindpats:
66 for kind, pat, source in kindpats:
67 if kind == 'subinclude':
67 if kind == 'subinclude':
68 sourceroot = pathutil.dirname(util.normpath(source))
68 sourceroot = pathutil.dirname(util.normpath(source))
69 pat = util.pconvert(pat)
69 pat = util.pconvert(pat)
70 path = pathutil.join(sourceroot, pat)
70 path = pathutil.join(sourceroot, pat)
71
71
72 newroot = pathutil.dirname(path)
72 newroot = pathutil.dirname(path)
73 matcherargs = (newroot, '', [], ['include:%s' % path])
73 matcherargs = (newroot, '', [], ['include:%s' % path])
74
74
75 prefix = pathutil.canonpath(root, root, newroot)
75 prefix = pathutil.canonpath(root, root, newroot)
76 if prefix:
76 if prefix:
77 prefix += '/'
77 prefix += '/'
78 relmatchers.append((prefix, matcherargs))
78 relmatchers.append((prefix, matcherargs))
79 else:
79 else:
80 other.append((kind, pat, source))
80 other.append((kind, pat, source))
81
81
82 return relmatchers, other
82 return relmatchers, other
83
83
84 def _kindpatsalwaysmatch(kindpats):
84 def _kindpatsalwaysmatch(kindpats):
85 """"Checks whether the kindspats match everything, as e.g.
85 """"Checks whether the kindspats match everything, as e.g.
86 'relpath:.' does.
86 'relpath:.' does.
87 """
87 """
88 for kind, pat, source in kindpats:
88 for kind, pat, source in kindpats:
89 if pat != '' or kind not in ['relpath', 'glob']:
89 if pat != '' or kind not in ['relpath', 'glob']:
90 return False
90 return False
91 return True
91 return True
92
92
93 def match(root, cwd, patterns=None, include=None, exclude=None, default='glob',
93 def match(root, cwd, patterns=None, include=None, exclude=None, default='glob',
94 exact=False, auditor=None, ctx=None, listsubrepos=False, warn=None,
94 exact=False, auditor=None, ctx=None, listsubrepos=False, warn=None,
95 badfn=None, icasefs=False):
95 badfn=None, icasefs=False):
96 """build an object to match a set of file patterns
96 """build an object to match a set of file patterns
97
97
98 arguments:
98 arguments:
99 root - the canonical root of the tree you're matching against
99 root - the canonical root of the tree you're matching against
100 cwd - the current working directory, if relevant
100 cwd - the current working directory, if relevant
101 patterns - patterns to find
101 patterns - patterns to find
102 include - patterns to include (unless they are excluded)
102 include - patterns to include (unless they are excluded)
103 exclude - patterns to exclude (even if they are included)
103 exclude - patterns to exclude (even if they are included)
104 default - if a pattern in patterns has no explicit type, assume this one
104 default - if a pattern in patterns has no explicit type, assume this one
105 exact - patterns are actually filenames (include/exclude still apply)
105 exact - patterns are actually filenames (include/exclude still apply)
106 warn - optional function used for printing warnings
106 warn - optional function used for printing warnings
107 badfn - optional bad() callback for this matcher instead of the default
107 badfn - optional bad() callback for this matcher instead of the default
108 icasefs - make a matcher for wdir on case insensitive filesystems, which
108 icasefs - make a matcher for wdir on case insensitive filesystems, which
109 normalizes the given patterns to the case in the filesystem
109 normalizes the given patterns to the case in the filesystem
110
110
111 a pattern is one of:
111 a pattern is one of:
112 'glob:<glob>' - a glob relative to cwd
112 'glob:<glob>' - a glob relative to cwd
113 're:<regexp>' - a regular expression
113 're:<regexp>' - a regular expression
114 'path:<path>' - a path relative to repository root, which is matched
114 'path:<path>' - a path relative to repository root, which is matched
115 recursively
115 recursively
116 'rootfilesin:<path>' - a path relative to repository root, which is
116 'rootfilesin:<path>' - a path relative to repository root, which is
117 matched non-recursively (will not match subdirectories)
117 matched non-recursively (will not match subdirectories)
118 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
118 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
119 'relpath:<path>' - a path relative to cwd
119 'relpath:<path>' - a path relative to cwd
120 'relre:<regexp>' - a regexp that needn't match the start of a name
120 'relre:<regexp>' - a regexp that needn't match the start of a name
121 'set:<fileset>' - a fileset expression
121 'set:<fileset>' - a fileset expression
122 'include:<path>' - a file of patterns to read and include
122 'include:<path>' - a file of patterns to read and include
123 'subinclude:<path>' - a file of patterns to match against files under
123 'subinclude:<path>' - a file of patterns to match against files under
124 the same directory
124 the same directory
125 '<something>' - a pattern of the specified default type
125 '<something>' - a pattern of the specified default type
126 """
126 """
127 normalize = _donormalize
127 normalize = _donormalize
128 if icasefs:
128 if icasefs:
129 if exact:
129 if exact:
130 raise error.ProgrammingError("a case-insensitive exact matcher "
130 raise error.ProgrammingError("a case-insensitive exact matcher "
131 "doesn't make sense")
131 "doesn't make sense")
132 dirstate = ctx.repo().dirstate
132 dirstate = ctx.repo().dirstate
133 dsnormalize = dirstate.normalize
133 dsnormalize = dirstate.normalize
134
134
135 def normalize(patterns, default, root, cwd, auditor, warn):
135 def normalize(patterns, default, root, cwd, auditor, warn):
136 kp = _donormalize(patterns, default, root, cwd, auditor, warn)
136 kp = _donormalize(patterns, default, root, cwd, auditor, warn)
137 kindpats = []
137 kindpats = []
138 for kind, pats, source in kp:
138 for kind, pats, source in kp:
139 if kind not in ('re', 'relre'): # regex can't be normalized
139 if kind not in ('re', 'relre'): # regex can't be normalized
140 p = pats
140 p = pats
141 pats = dsnormalize(pats)
141 pats = dsnormalize(pats)
142
142
143 # Preserve the original to handle a case only rename.
143 # Preserve the original to handle a case only rename.
144 if p != pats and p in dirstate:
144 if p != pats and p in dirstate:
145 kindpats.append((kind, p, source))
145 kindpats.append((kind, p, source))
146
146
147 kindpats.append((kind, pats, source))
147 kindpats.append((kind, pats, source))
148 return kindpats
148 return kindpats
149
149
150 if exact:
150 if exact:
151 m = exactmatcher(root, cwd, patterns, badfn)
151 m = exactmatcher(root, cwd, patterns, badfn)
152 elif patterns:
152 elif patterns:
153 kindpats = normalize(patterns, default, root, cwd, auditor, warn)
153 kindpats = normalize(patterns, default, root, cwd, auditor, warn)
154 if _kindpatsalwaysmatch(kindpats):
154 if _kindpatsalwaysmatch(kindpats):
155 m = alwaysmatcher(root, cwd, badfn, relativeuipath=True)
155 m = alwaysmatcher(root, cwd, badfn, relativeuipath=True)
156 else:
156 else:
157 m = patternmatcher(root, cwd, kindpats, ctx=ctx,
157 m = patternmatcher(root, cwd, kindpats, ctx=ctx,
158 listsubrepos=listsubrepos, badfn=badfn)
158 listsubrepos=listsubrepos, badfn=badfn)
159 else:
159 else:
160 # It's a little strange that no patterns means to match everything.
160 # It's a little strange that no patterns means to match everything.
161 # Consider changing this to match nothing (probably using nevermatcher).
161 # Consider changing this to match nothing (probably using nevermatcher).
162 m = alwaysmatcher(root, cwd, badfn)
162 m = alwaysmatcher(root, cwd, badfn)
163
163
164 if include:
164 if include:
165 kindpats = normalize(include, 'glob', root, cwd, auditor, warn)
165 kindpats = normalize(include, 'glob', root, cwd, auditor, warn)
166 im = includematcher(root, cwd, kindpats, ctx=ctx,
166 im = includematcher(root, cwd, kindpats, ctx=ctx,
167 listsubrepos=listsubrepos, badfn=None)
167 listsubrepos=listsubrepos, badfn=None)
168 m = intersectmatchers(m, im)
168 m = intersectmatchers(m, im)
169 if exclude:
169 if exclude:
170 kindpats = normalize(exclude, 'glob', root, cwd, auditor, warn)
170 kindpats = normalize(exclude, 'glob', root, cwd, auditor, warn)
171 em = includematcher(root, cwd, kindpats, ctx=ctx,
171 em = includematcher(root, cwd, kindpats, ctx=ctx,
172 listsubrepos=listsubrepos, badfn=None)
172 listsubrepos=listsubrepos, badfn=None)
173 m = differencematcher(m, em)
173 m = differencematcher(m, em)
174 return m
174 return m
175
175
176 def exact(root, cwd, files, badfn=None):
176 def exact(root, cwd, files, badfn=None):
177 return exactmatcher(root, cwd, files, badfn=badfn)
177 return exactmatcher(root, cwd, files, badfn=badfn)
178
178
179 def always(root, cwd):
179 def always(root, cwd):
180 return alwaysmatcher(root, cwd)
180 return alwaysmatcher(root, cwd)
181
181
182 def never(root, cwd):
182 def never(root, cwd):
183 return nevermatcher(root, cwd)
183 return nevermatcher(root, cwd)
184
184
185 def badmatch(match, badfn):
185 def badmatch(match, badfn):
186 """Make a copy of the given matcher, replacing its bad method with the given
186 """Make a copy of the given matcher, replacing its bad method with the given
187 one.
187 one.
188 """
188 """
189 m = copy.copy(match)
189 m = copy.copy(match)
190 m.bad = badfn
190 m.bad = badfn
191 return m
191 return m
192
192
193 def _donormalize(patterns, default, root, cwd, auditor, warn):
193 def _donormalize(patterns, default, root, cwd, auditor, warn):
194 '''Convert 'kind:pat' from the patterns list to tuples with kind and
194 '''Convert 'kind:pat' from the patterns list to tuples with kind and
195 normalized and rooted patterns and with listfiles expanded.'''
195 normalized and rooted patterns and with listfiles expanded.'''
196 kindpats = []
196 kindpats = []
197 for kind, pat in [_patsplit(p, default) for p in patterns]:
197 for kind, pat in [_patsplit(p, default) for p in patterns]:
198 if kind in cwdrelativepatternkinds:
198 if kind in cwdrelativepatternkinds:
199 pat = pathutil.canonpath(root, cwd, pat, auditor)
199 pat = pathutil.canonpath(root, cwd, pat, auditor)
200 elif kind in ('relglob', 'path', 'rootfilesin'):
200 elif kind in ('relglob', 'path', 'rootfilesin'):
201 pat = util.normpath(pat)
201 pat = util.normpath(pat)
202 elif kind in ('listfile', 'listfile0'):
202 elif kind in ('listfile', 'listfile0'):
203 try:
203 try:
204 files = util.readfile(pat)
204 files = util.readfile(pat)
205 if kind == 'listfile0':
205 if kind == 'listfile0':
206 files = files.split('\0')
206 files = files.split('\0')
207 else:
207 else:
208 files = files.splitlines()
208 files = files.splitlines()
209 files = [f for f in files if f]
209 files = [f for f in files if f]
210 except EnvironmentError:
210 except EnvironmentError:
211 raise error.Abort(_("unable to read file list (%s)") % pat)
211 raise error.Abort(_("unable to read file list (%s)") % pat)
212 for k, p, source in _donormalize(files, default, root, cwd,
212 for k, p, source in _donormalize(files, default, root, cwd,
213 auditor, warn):
213 auditor, warn):
214 kindpats.append((k, p, pat))
214 kindpats.append((k, p, pat))
215 continue
215 continue
216 elif kind == 'include':
216 elif kind == 'include':
217 try:
217 try:
218 fullpath = os.path.join(root, util.localpath(pat))
218 fullpath = os.path.join(root, util.localpath(pat))
219 includepats = readpatternfile(fullpath, warn)
219 includepats = readpatternfile(fullpath, warn)
220 for k, p, source in _donormalize(includepats, default,
220 for k, p, source in _donormalize(includepats, default,
221 root, cwd, auditor, warn):
221 root, cwd, auditor, warn):
222 kindpats.append((k, p, source or pat))
222 kindpats.append((k, p, source or pat))
223 except error.Abort as inst:
223 except error.Abort as inst:
224 raise error.Abort('%s: %s' % (pat, inst[0]))
224 raise error.Abort('%s: %s' % (pat, inst[0]))
225 except IOError as inst:
225 except IOError as inst:
226 if warn:
226 if warn:
227 warn(_("skipping unreadable pattern file '%s': %s\n") %
227 warn(_("skipping unreadable pattern file '%s': %s\n") %
228 (pat, inst.strerror))
228 (pat, inst.strerror))
229 continue
229 continue
230 # else: re or relre - which cannot be normalized
230 # else: re or relre - which cannot be normalized
231 kindpats.append((kind, pat, ''))
231 kindpats.append((kind, pat, ''))
232 return kindpats
232 return kindpats
233
233
234 class basematcher(object):
234 class basematcher(object):
235
235
236 def __init__(self, root, cwd, badfn=None, relativeuipath=True):
236 def __init__(self, root, cwd, badfn=None, relativeuipath=True):
237 self._root = root
237 self._root = root
238 self._cwd = cwd
238 self._cwd = cwd
239 if badfn is not None:
239 if badfn is not None:
240 self.bad = badfn
240 self.bad = badfn
241 self._relativeuipath = relativeuipath
241 self._relativeuipath = relativeuipath
242
242
243 def __call__(self, fn):
243 def __call__(self, fn):
244 return self.matchfn(fn)
244 return self.matchfn(fn)
245 def __iter__(self):
245 def __iter__(self):
246 for f in self._files:
246 for f in self._files:
247 yield f
247 yield f
248 # Callbacks related to how the matcher is used by dirstate.walk.
248 # Callbacks related to how the matcher is used by dirstate.walk.
249 # Subscribers to these events must monkeypatch the matcher object.
249 # Subscribers to these events must monkeypatch the matcher object.
250 def bad(self, f, msg):
250 def bad(self, f, msg):
251 '''Callback from dirstate.walk for each explicit file that can't be
251 '''Callback from dirstate.walk for each explicit file that can't be
252 found/accessed, with an error message.'''
252 found/accessed, with an error message.'''
253 pass
254
253
255 # If an explicitdir is set, it will be called when an explicitly listed
254 # If an explicitdir is set, it will be called when an explicitly listed
256 # directory is visited.
255 # directory is visited.
257 explicitdir = None
256 explicitdir = None
258
257
259 # If an traversedir is set, it will be called when a directory discovered
258 # If an traversedir is set, it will be called when a directory discovered
260 # by recursive traversal is visited.
259 # by recursive traversal is visited.
261 traversedir = None
260 traversedir = None
262
261
263 def abs(self, f):
262 def abs(self, f):
264 '''Convert a repo path back to path that is relative to the root of the
263 '''Convert a repo path back to path that is relative to the root of the
265 matcher.'''
264 matcher.'''
266 return f
265 return f
267
266
268 def rel(self, f):
267 def rel(self, f):
269 '''Convert repo path back to path that is relative to cwd of matcher.'''
268 '''Convert repo path back to path that is relative to cwd of matcher.'''
270 return util.pathto(self._root, self._cwd, f)
269 return util.pathto(self._root, self._cwd, f)
271
270
272 def uipath(self, f):
271 def uipath(self, f):
273 '''Convert repo path to a display path. If patterns or -I/-X were used
272 '''Convert repo path to a display path. If patterns or -I/-X were used
274 to create this matcher, the display path will be relative to cwd.
273 to create this matcher, the display path will be relative to cwd.
275 Otherwise it is relative to the root of the repo.'''
274 Otherwise it is relative to the root of the repo.'''
276 return (self._relativeuipath and self.rel(f)) or self.abs(f)
275 return (self._relativeuipath and self.rel(f)) or self.abs(f)
277
276
278 @propertycache
277 @propertycache
279 def _files(self):
278 def _files(self):
280 return []
279 return []
281
280
282 def files(self):
281 def files(self):
283 '''Explicitly listed files or patterns or roots:
282 '''Explicitly listed files or patterns or roots:
284 if no patterns or .always(): empty list,
283 if no patterns or .always(): empty list,
285 if exact: list exact files,
284 if exact: list exact files,
286 if not .anypats(): list all files and dirs,
285 if not .anypats(): list all files and dirs,
287 else: optimal roots'''
286 else: optimal roots'''
288 return self._files
287 return self._files
289
288
290 @propertycache
289 @propertycache
291 def _fileset(self):
290 def _fileset(self):
292 return set(self._files)
291 return set(self._files)
293
292
294 def exact(self, f):
293 def exact(self, f):
295 '''Returns True if f is in .files().'''
294 '''Returns True if f is in .files().'''
296 return f in self._fileset
295 return f in self._fileset
297
296
298 def matchfn(self, f):
297 def matchfn(self, f):
299 return False
298 return False
300
299
301 def visitdir(self, dir):
300 def visitdir(self, dir):
302 '''Decides whether a directory should be visited based on whether it
301 '''Decides whether a directory should be visited based on whether it
303 has potential matches in it or one of its subdirectories. This is
302 has potential matches in it or one of its subdirectories. This is
304 based on the match's primary, included, and excluded patterns.
303 based on the match's primary, included, and excluded patterns.
305
304
306 Returns the string 'all' if the given directory and all subdirectories
305 Returns the string 'all' if the given directory and all subdirectories
307 should be visited. Otherwise returns True or False indicating whether
306 should be visited. Otherwise returns True or False indicating whether
308 the given directory should be visited.
307 the given directory should be visited.
309
308
310 This function's behavior is undefined if it has returned False for
309 This function's behavior is undefined if it has returned False for
311 one of the dir's parent directories.
310 one of the dir's parent directories.
312 '''
311 '''
313 return True
312 return True
314
313
315 def always(self):
314 def always(self):
316 '''Matcher will match everything and .files() will be empty --
315 '''Matcher will match everything and .files() will be empty --
317 optimization might be possible.'''
316 optimization might be possible.'''
318 return False
317 return False
319
318
320 def isexact(self):
319 def isexact(self):
321 '''Matcher will match exactly the list of files in .files() --
320 '''Matcher will match exactly the list of files in .files() --
322 optimization might be possible.'''
321 optimization might be possible.'''
323 return False
322 return False
324
323
325 def prefix(self):
324 def prefix(self):
326 '''Matcher will match the paths in .files() recursively --
325 '''Matcher will match the paths in .files() recursively --
327 optimization might be possible.'''
326 optimization might be possible.'''
328 return False
327 return False
329
328
330 def anypats(self):
329 def anypats(self):
331 '''None of .always(), .isexact(), and .prefix() is true --
330 '''None of .always(), .isexact(), and .prefix() is true --
332 optimizations will be difficult.'''
331 optimizations will be difficult.'''
333 return not self.always() and not self.isexact() and not self.prefix()
332 return not self.always() and not self.isexact() and not self.prefix()
334
333
335 class alwaysmatcher(basematcher):
334 class alwaysmatcher(basematcher):
336 '''Matches everything.'''
335 '''Matches everything.'''
337
336
338 def __init__(self, root, cwd, badfn=None, relativeuipath=False):
337 def __init__(self, root, cwd, badfn=None, relativeuipath=False):
339 super(alwaysmatcher, self).__init__(root, cwd, badfn,
338 super(alwaysmatcher, self).__init__(root, cwd, badfn,
340 relativeuipath=relativeuipath)
339 relativeuipath=relativeuipath)
341
340
342 def always(self):
341 def always(self):
343 return True
342 return True
344
343
345 def matchfn(self, f):
344 def matchfn(self, f):
346 return True
345 return True
347
346
348 def visitdir(self, dir):
347 def visitdir(self, dir):
349 return 'all'
348 return 'all'
350
349
351 def __repr__(self):
350 def __repr__(self):
352 return '<alwaysmatcher>'
351 return '<alwaysmatcher>'
353
352
354 class nevermatcher(basematcher):
353 class nevermatcher(basematcher):
355 '''Matches nothing.'''
354 '''Matches nothing.'''
356
355
357 def __init__(self, root, cwd, badfn=None):
356 def __init__(self, root, cwd, badfn=None):
358 super(nevermatcher, self).__init__(root, cwd, badfn)
357 super(nevermatcher, self).__init__(root, cwd, badfn)
359
358
360 # It's a little weird to say that the nevermatcher is an exact matcher
359 # It's a little weird to say that the nevermatcher is an exact matcher
361 # or a prefix matcher, but it seems to make sense to let callers take
360 # or a prefix matcher, but it seems to make sense to let callers take
362 # fast paths based on either. There will be no exact matches, nor any
361 # fast paths based on either. There will be no exact matches, nor any
363 # prefixes (files() returns []), so fast paths iterating over them should
362 # prefixes (files() returns []), so fast paths iterating over them should
364 # be efficient (and correct).
363 # be efficient (and correct).
365 def isexact(self):
364 def isexact(self):
366 return True
365 return True
367
366
368 def prefix(self):
367 def prefix(self):
369 return True
368 return True
370
369
371 def visitdir(self, dir):
370 def visitdir(self, dir):
372 return False
371 return False
373
372
374 def __repr__(self):
373 def __repr__(self):
375 return '<nevermatcher>'
374 return '<nevermatcher>'
376
375
377 class patternmatcher(basematcher):
376 class patternmatcher(basematcher):
378
377
379 def __init__(self, root, cwd, kindpats, ctx=None, listsubrepos=False,
378 def __init__(self, root, cwd, kindpats, ctx=None, listsubrepos=False,
380 badfn=None):
379 badfn=None):
381 super(patternmatcher, self).__init__(root, cwd, badfn)
380 super(patternmatcher, self).__init__(root, cwd, badfn)
382
381
383 self._files = _explicitfiles(kindpats)
382 self._files = _explicitfiles(kindpats)
384 self._prefix = _prefix(kindpats)
383 self._prefix = _prefix(kindpats)
385 self._pats, self.matchfn = _buildmatch(ctx, kindpats, '$', listsubrepos,
384 self._pats, self.matchfn = _buildmatch(ctx, kindpats, '$', listsubrepos,
386 root)
385 root)
387
386
388 @propertycache
387 @propertycache
389 def _dirs(self):
388 def _dirs(self):
390 return set(util.dirs(self._fileset)) | {'.'}
389 return set(util.dirs(self._fileset)) | {'.'}
391
390
392 def visitdir(self, dir):
391 def visitdir(self, dir):
393 if self._prefix and dir in self._fileset:
392 if self._prefix and dir in self._fileset:
394 return 'all'
393 return 'all'
395 return ('.' in self._fileset or
394 return ('.' in self._fileset or
396 dir in self._fileset or
395 dir in self._fileset or
397 dir in self._dirs or
396 dir in self._dirs or
398 any(parentdir in self._fileset
397 any(parentdir in self._fileset
399 for parentdir in util.finddirs(dir)))
398 for parentdir in util.finddirs(dir)))
400
399
401 def prefix(self):
400 def prefix(self):
402 return self._prefix
401 return self._prefix
403
402
404 def __repr__(self):
403 def __repr__(self):
405 return ('<patternmatcher patterns=%r>' % self._pats)
404 return ('<patternmatcher patterns=%r>' % self._pats)
406
405
407 class includematcher(basematcher):
406 class includematcher(basematcher):
408
407
409 def __init__(self, root, cwd, kindpats, ctx=None, listsubrepos=False,
408 def __init__(self, root, cwd, kindpats, ctx=None, listsubrepos=False,
410 badfn=None):
409 badfn=None):
411 super(includematcher, self).__init__(root, cwd, badfn)
410 super(includematcher, self).__init__(root, cwd, badfn)
412
411
413 self._pats, self.matchfn = _buildmatch(ctx, kindpats, '(?:/|$)',
412 self._pats, self.matchfn = _buildmatch(ctx, kindpats, '(?:/|$)',
414 listsubrepos, root)
413 listsubrepos, root)
415 self._prefix = _prefix(kindpats)
414 self._prefix = _prefix(kindpats)
416 roots, dirs = _rootsanddirs(kindpats)
415 roots, dirs = _rootsanddirs(kindpats)
417 # roots are directories which are recursively included.
416 # roots are directories which are recursively included.
418 self._roots = set(roots)
417 self._roots = set(roots)
419 # dirs are directories which are non-recursively included.
418 # dirs are directories which are non-recursively included.
420 self._dirs = set(dirs)
419 self._dirs = set(dirs)
421
420
422 def visitdir(self, dir):
421 def visitdir(self, dir):
423 if self._prefix and dir in self._roots:
422 if self._prefix and dir in self._roots:
424 return 'all'
423 return 'all'
425 return ('.' in self._roots or
424 return ('.' in self._roots or
426 dir in self._roots or
425 dir in self._roots or
427 dir in self._dirs or
426 dir in self._dirs or
428 any(parentdir in self._roots
427 any(parentdir in self._roots
429 for parentdir in util.finddirs(dir)))
428 for parentdir in util.finddirs(dir)))
430
429
431 def __repr__(self):
430 def __repr__(self):
432 return ('<includematcher includes=%r>' % self._pats)
431 return ('<includematcher includes=%r>' % self._pats)
433
432
434 class exactmatcher(basematcher):
433 class exactmatcher(basematcher):
435 '''Matches the input files exactly. They are interpreted as paths, not
434 '''Matches the input files exactly. They are interpreted as paths, not
436 patterns (so no kind-prefixes).
435 patterns (so no kind-prefixes).
437 '''
436 '''
438
437
439 def __init__(self, root, cwd, files, badfn=None):
438 def __init__(self, root, cwd, files, badfn=None):
440 super(exactmatcher, self).__init__(root, cwd, badfn)
439 super(exactmatcher, self).__init__(root, cwd, badfn)
441
440
442 if isinstance(files, list):
441 if isinstance(files, list):
443 self._files = files
442 self._files = files
444 else:
443 else:
445 self._files = list(files)
444 self._files = list(files)
446
445
447 matchfn = basematcher.exact
446 matchfn = basematcher.exact
448
447
449 @propertycache
448 @propertycache
450 def _dirs(self):
449 def _dirs(self):
451 return set(util.dirs(self._fileset)) | {'.'}
450 return set(util.dirs(self._fileset)) | {'.'}
452
451
453 def visitdir(self, dir):
452 def visitdir(self, dir):
454 return dir in self._dirs
453 return dir in self._dirs
455
454
456 def isexact(self):
455 def isexact(self):
457 return True
456 return True
458
457
459 def __repr__(self):
458 def __repr__(self):
460 return ('<exactmatcher files=%r>' % self._files)
459 return ('<exactmatcher files=%r>' % self._files)
461
460
462 class differencematcher(basematcher):
461 class differencematcher(basematcher):
463 '''Composes two matchers by matching if the first matches and the second
462 '''Composes two matchers by matching if the first matches and the second
464 does not. Well, almost... If the user provides a pattern like "-X foo foo",
463 does not. Well, almost... If the user provides a pattern like "-X foo foo",
465 Mercurial actually does match "foo" against that. That's because exact
464 Mercurial actually does match "foo" against that. That's because exact
466 matches are treated specially. So, since this differencematcher is used for
465 matches are treated specially. So, since this differencematcher is used for
467 excludes, it needs to special-case exact matching.
466 excludes, it needs to special-case exact matching.
468
467
469 The second matcher's non-matching-attributes (root, cwd, bad, explicitdir,
468 The second matcher's non-matching-attributes (root, cwd, bad, explicitdir,
470 traversedir) are ignored.
469 traversedir) are ignored.
471
470
472 TODO: If we want to keep the behavior described above for exact matches, we
471 TODO: If we want to keep the behavior described above for exact matches, we
473 should consider instead treating the above case something like this:
472 should consider instead treating the above case something like this:
474 union(exact(foo), difference(pattern(foo), include(foo)))
473 union(exact(foo), difference(pattern(foo), include(foo)))
475 '''
474 '''
476 def __init__(self, m1, m2):
475 def __init__(self, m1, m2):
477 super(differencematcher, self).__init__(m1._root, m1._cwd)
476 super(differencematcher, self).__init__(m1._root, m1._cwd)
478 self._m1 = m1
477 self._m1 = m1
479 self._m2 = m2
478 self._m2 = m2
480 self.bad = m1.bad
479 self.bad = m1.bad
481 self.explicitdir = m1.explicitdir
480 self.explicitdir = m1.explicitdir
482 self.traversedir = m1.traversedir
481 self.traversedir = m1.traversedir
483
482
484 def matchfn(self, f):
483 def matchfn(self, f):
485 return self._m1(f) and (not self._m2(f) or self._m1.exact(f))
484 return self._m1(f) and (not self._m2(f) or self._m1.exact(f))
486
485
487 @propertycache
486 @propertycache
488 def _files(self):
487 def _files(self):
489 if self.isexact():
488 if self.isexact():
490 return [f for f in self._m1.files() if self(f)]
489 return [f for f in self._m1.files() if self(f)]
491 # If m1 is not an exact matcher, we can't easily figure out the set of
490 # If m1 is not an exact matcher, we can't easily figure out the set of
492 # files, because its files() are not always files. For example, if
491 # files, because its files() are not always files. For example, if
493 # m1 is "path:dir" and m2 is "rootfileins:.", we don't
492 # m1 is "path:dir" and m2 is "rootfileins:.", we don't
494 # want to remove "dir" from the set even though it would match m2,
493 # want to remove "dir" from the set even though it would match m2,
495 # because the "dir" in m1 may not be a file.
494 # because the "dir" in m1 may not be a file.
496 return self._m1.files()
495 return self._m1.files()
497
496
498 def visitdir(self, dir):
497 def visitdir(self, dir):
499 if self._m2.visitdir(dir) == 'all':
498 if self._m2.visitdir(dir) == 'all':
500 # There's a bug here: If m1 matches file 'dir/file' and m2 excludes
499 # There's a bug here: If m1 matches file 'dir/file' and m2 excludes
501 # 'dir' (recursively), we should still visit 'dir' due to the
500 # 'dir' (recursively), we should still visit 'dir' due to the
502 # exception we have for exact matches.
501 # exception we have for exact matches.
503 return False
502 return False
504 return bool(self._m1.visitdir(dir))
503 return bool(self._m1.visitdir(dir))
505
504
506 def isexact(self):
505 def isexact(self):
507 return self._m1.isexact()
506 return self._m1.isexact()
508
507
509 def __repr__(self):
508 def __repr__(self):
510 return ('<differencematcher m1=%r, m2=%r>' % (self._m1, self._m2))
509 return ('<differencematcher m1=%r, m2=%r>' % (self._m1, self._m2))
511
510
512 def intersectmatchers(m1, m2):
511 def intersectmatchers(m1, m2):
513 '''Composes two matchers by matching if both of them match.
512 '''Composes two matchers by matching if both of them match.
514
513
515 The second matcher's non-matching-attributes (root, cwd, bad, explicitdir,
514 The second matcher's non-matching-attributes (root, cwd, bad, explicitdir,
516 traversedir) are ignored.
515 traversedir) are ignored.
517 '''
516 '''
518 if m1 is None or m2 is None:
517 if m1 is None or m2 is None:
519 return m1 or m2
518 return m1 or m2
520 if m1.always():
519 if m1.always():
521 m = copy.copy(m2)
520 m = copy.copy(m2)
522 # TODO: Consider encapsulating these things in a class so there's only
521 # TODO: Consider encapsulating these things in a class so there's only
523 # one thing to copy from m1.
522 # one thing to copy from m1.
524 m.bad = m1.bad
523 m.bad = m1.bad
525 m.explicitdir = m1.explicitdir
524 m.explicitdir = m1.explicitdir
526 m.traversedir = m1.traversedir
525 m.traversedir = m1.traversedir
527 m.abs = m1.abs
526 m.abs = m1.abs
528 m.rel = m1.rel
527 m.rel = m1.rel
529 m._relativeuipath |= m1._relativeuipath
528 m._relativeuipath |= m1._relativeuipath
530 return m
529 return m
531 if m2.always():
530 if m2.always():
532 m = copy.copy(m1)
531 m = copy.copy(m1)
533 m._relativeuipath |= m2._relativeuipath
532 m._relativeuipath |= m2._relativeuipath
534 return m
533 return m
535 return intersectionmatcher(m1, m2)
534 return intersectionmatcher(m1, m2)
536
535
537 class intersectionmatcher(basematcher):
536 class intersectionmatcher(basematcher):
538 def __init__(self, m1, m2):
537 def __init__(self, m1, m2):
539 super(intersectionmatcher, self).__init__(m1._root, m1._cwd)
538 super(intersectionmatcher, self).__init__(m1._root, m1._cwd)
540 self._m1 = m1
539 self._m1 = m1
541 self._m2 = m2
540 self._m2 = m2
542 self.bad = m1.bad
541 self.bad = m1.bad
543 self.explicitdir = m1.explicitdir
542 self.explicitdir = m1.explicitdir
544 self.traversedir = m1.traversedir
543 self.traversedir = m1.traversedir
545
544
546 @propertycache
545 @propertycache
547 def _files(self):
546 def _files(self):
548 if self.isexact():
547 if self.isexact():
549 m1, m2 = self._m1, self._m2
548 m1, m2 = self._m1, self._m2
550 if not m1.isexact():
549 if not m1.isexact():
551 m1, m2 = m2, m1
550 m1, m2 = m2, m1
552 return [f for f in m1.files() if m2(f)]
551 return [f for f in m1.files() if m2(f)]
553 # It neither m1 nor m2 is an exact matcher, we can't easily intersect
552 # It neither m1 nor m2 is an exact matcher, we can't easily intersect
554 # the set of files, because their files() are not always files. For
553 # the set of files, because their files() are not always files. For
555 # example, if intersecting a matcher "-I glob:foo.txt" with matcher of
554 # example, if intersecting a matcher "-I glob:foo.txt" with matcher of
556 # "path:dir2", we don't want to remove "dir2" from the set.
555 # "path:dir2", we don't want to remove "dir2" from the set.
557 return self._m1.files() + self._m2.files()
556 return self._m1.files() + self._m2.files()
558
557
559 def matchfn(self, f):
558 def matchfn(self, f):
560 return self._m1(f) and self._m2(f)
559 return self._m1(f) and self._m2(f)
561
560
562 def visitdir(self, dir):
561 def visitdir(self, dir):
563 visit1 = self._m1.visitdir(dir)
562 visit1 = self._m1.visitdir(dir)
564 if visit1 == 'all':
563 if visit1 == 'all':
565 return self._m2.visitdir(dir)
564 return self._m2.visitdir(dir)
566 # bool() because visit1=True + visit2='all' should not be 'all'
565 # bool() because visit1=True + visit2='all' should not be 'all'
567 return bool(visit1 and self._m2.visitdir(dir))
566 return bool(visit1 and self._m2.visitdir(dir))
568
567
569 def always(self):
568 def always(self):
570 return self._m1.always() and self._m2.always()
569 return self._m1.always() and self._m2.always()
571
570
572 def isexact(self):
571 def isexact(self):
573 return self._m1.isexact() or self._m2.isexact()
572 return self._m1.isexact() or self._m2.isexact()
574
573
575 def __repr__(self):
574 def __repr__(self):
576 return ('<intersectionmatcher m1=%r, m2=%r>' % (self._m1, self._m2))
575 return ('<intersectionmatcher m1=%r, m2=%r>' % (self._m1, self._m2))
577
576
578 class subdirmatcher(basematcher):
577 class subdirmatcher(basematcher):
579 """Adapt a matcher to work on a subdirectory only.
578 """Adapt a matcher to work on a subdirectory only.
580
579
581 The paths are remapped to remove/insert the path as needed:
580 The paths are remapped to remove/insert the path as needed:
582
581
583 >>> from . import pycompat
582 >>> from . import pycompat
584 >>> m1 = match(b'root', b'', [b'a.txt', b'sub/b.txt'])
583 >>> m1 = match(b'root', b'', [b'a.txt', b'sub/b.txt'])
585 >>> m2 = subdirmatcher(b'sub', m1)
584 >>> m2 = subdirmatcher(b'sub', m1)
586 >>> bool(m2(b'a.txt'))
585 >>> bool(m2(b'a.txt'))
587 False
586 False
588 >>> bool(m2(b'b.txt'))
587 >>> bool(m2(b'b.txt'))
589 True
588 True
590 >>> bool(m2.matchfn(b'a.txt'))
589 >>> bool(m2.matchfn(b'a.txt'))
591 False
590 False
592 >>> bool(m2.matchfn(b'b.txt'))
591 >>> bool(m2.matchfn(b'b.txt'))
593 True
592 True
594 >>> m2.files()
593 >>> m2.files()
595 ['b.txt']
594 ['b.txt']
596 >>> m2.exact(b'b.txt')
595 >>> m2.exact(b'b.txt')
597 True
596 True
598 >>> util.pconvert(m2.rel(b'b.txt'))
597 >>> util.pconvert(m2.rel(b'b.txt'))
599 'sub/b.txt'
598 'sub/b.txt'
600 >>> def bad(f, msg):
599 >>> def bad(f, msg):
601 ... print(pycompat.sysstr(b"%s: %s" % (f, msg)))
600 ... print(pycompat.sysstr(b"%s: %s" % (f, msg)))
602 >>> m1.bad = bad
601 >>> m1.bad = bad
603 >>> m2.bad(b'x.txt', b'No such file')
602 >>> m2.bad(b'x.txt', b'No such file')
604 sub/x.txt: No such file
603 sub/x.txt: No such file
605 >>> m2.abs(b'c.txt')
604 >>> m2.abs(b'c.txt')
606 'sub/c.txt'
605 'sub/c.txt'
607 """
606 """
608
607
609 def __init__(self, path, matcher):
608 def __init__(self, path, matcher):
610 super(subdirmatcher, self).__init__(matcher._root, matcher._cwd)
609 super(subdirmatcher, self).__init__(matcher._root, matcher._cwd)
611 self._path = path
610 self._path = path
612 self._matcher = matcher
611 self._matcher = matcher
613 self._always = matcher.always()
612 self._always = matcher.always()
614
613
615 self._files = [f[len(path) + 1:] for f in matcher._files
614 self._files = [f[len(path) + 1:] for f in matcher._files
616 if f.startswith(path + "/")]
615 if f.startswith(path + "/")]
617
616
618 # If the parent repo had a path to this subrepo and the matcher is
617 # If the parent repo had a path to this subrepo and the matcher is
619 # a prefix matcher, this submatcher always matches.
618 # a prefix matcher, this submatcher always matches.
620 if matcher.prefix():
619 if matcher.prefix():
621 self._always = any(f == path for f in matcher._files)
620 self._always = any(f == path for f in matcher._files)
622
621
623 def bad(self, f, msg):
622 def bad(self, f, msg):
624 self._matcher.bad(self._path + "/" + f, msg)
623 self._matcher.bad(self._path + "/" + f, msg)
625
624
626 def abs(self, f):
625 def abs(self, f):
627 return self._matcher.abs(self._path + "/" + f)
626 return self._matcher.abs(self._path + "/" + f)
628
627
629 def rel(self, f):
628 def rel(self, f):
630 return self._matcher.rel(self._path + "/" + f)
629 return self._matcher.rel(self._path + "/" + f)
631
630
632 def uipath(self, f):
631 def uipath(self, f):
633 return self._matcher.uipath(self._path + "/" + f)
632 return self._matcher.uipath(self._path + "/" + f)
634
633
635 def matchfn(self, f):
634 def matchfn(self, f):
636 # Some information is lost in the superclass's constructor, so we
635 # Some information is lost in the superclass's constructor, so we
637 # can not accurately create the matching function for the subdirectory
636 # can not accurately create the matching function for the subdirectory
638 # from the inputs. Instead, we override matchfn() and visitdir() to
637 # from the inputs. Instead, we override matchfn() and visitdir() to
639 # call the original matcher with the subdirectory path prepended.
638 # call the original matcher with the subdirectory path prepended.
640 return self._matcher.matchfn(self._path + "/" + f)
639 return self._matcher.matchfn(self._path + "/" + f)
641
640
642 def visitdir(self, dir):
641 def visitdir(self, dir):
643 if dir == '.':
642 if dir == '.':
644 dir = self._path
643 dir = self._path
645 else:
644 else:
646 dir = self._path + "/" + dir
645 dir = self._path + "/" + dir
647 return self._matcher.visitdir(dir)
646 return self._matcher.visitdir(dir)
648
647
649 def always(self):
648 def always(self):
650 return self._always
649 return self._always
651
650
652 def prefix(self):
651 def prefix(self):
653 return self._matcher.prefix() and not self._always
652 return self._matcher.prefix() and not self._always
654
653
655 def __repr__(self):
654 def __repr__(self):
656 return ('<subdirmatcher path=%r, matcher=%r>' %
655 return ('<subdirmatcher path=%r, matcher=%r>' %
657 (self._path, self._matcher))
656 (self._path, self._matcher))
658
657
659 class unionmatcher(basematcher):
658 class unionmatcher(basematcher):
660 """A matcher that is the union of several matchers.
659 """A matcher that is the union of several matchers.
661
660
662 The non-matching-attributes (root, cwd, bad, explicitdir, traversedir) are
661 The non-matching-attributes (root, cwd, bad, explicitdir, traversedir) are
663 taken from the first matcher.
662 taken from the first matcher.
664 """
663 """
665
664
666 def __init__(self, matchers):
665 def __init__(self, matchers):
667 m1 = matchers[0]
666 m1 = matchers[0]
668 super(unionmatcher, self).__init__(m1._root, m1._cwd)
667 super(unionmatcher, self).__init__(m1._root, m1._cwd)
669 self.explicitdir = m1.explicitdir
668 self.explicitdir = m1.explicitdir
670 self.traversedir = m1.traversedir
669 self.traversedir = m1.traversedir
671 self._matchers = matchers
670 self._matchers = matchers
672
671
673 def matchfn(self, f):
672 def matchfn(self, f):
674 for match in self._matchers:
673 for match in self._matchers:
675 if match(f):
674 if match(f):
676 return True
675 return True
677 return False
676 return False
678
677
679 def visitdir(self, dir):
678 def visitdir(self, dir):
680 r = False
679 r = False
681 for m in self._matchers:
680 for m in self._matchers:
682 v = m.visitdir(dir)
681 v = m.visitdir(dir)
683 if v == 'all':
682 if v == 'all':
684 return v
683 return v
685 r |= v
684 r |= v
686 return r
685 return r
687
686
688 def __repr__(self):
687 def __repr__(self):
689 return ('<unionmatcher matchers=%r>' % self._matchers)
688 return ('<unionmatcher matchers=%r>' % self._matchers)
690
689
691 def patkind(pattern, default=None):
690 def patkind(pattern, default=None):
692 '''If pattern is 'kind:pat' with a known kind, return kind.'''
691 '''If pattern is 'kind:pat' with a known kind, return kind.'''
693 return _patsplit(pattern, default)[0]
692 return _patsplit(pattern, default)[0]
694
693
695 def _patsplit(pattern, default):
694 def _patsplit(pattern, default):
696 """Split a string into the optional pattern kind prefix and the actual
695 """Split a string into the optional pattern kind prefix and the actual
697 pattern."""
696 pattern."""
698 if ':' in pattern:
697 if ':' in pattern:
699 kind, pat = pattern.split(':', 1)
698 kind, pat = pattern.split(':', 1)
700 if kind in allpatternkinds:
699 if kind in allpatternkinds:
701 return kind, pat
700 return kind, pat
702 return default, pattern
701 return default, pattern
703
702
704 def _globre(pat):
703 def _globre(pat):
705 r'''Convert an extended glob string to a regexp string.
704 r'''Convert an extended glob string to a regexp string.
706
705
707 >>> from . import pycompat
706 >>> from . import pycompat
708 >>> def bprint(s):
707 >>> def bprint(s):
709 ... print(pycompat.sysstr(s))
708 ... print(pycompat.sysstr(s))
710 >>> bprint(_globre(br'?'))
709 >>> bprint(_globre(br'?'))
711 .
710 .
712 >>> bprint(_globre(br'*'))
711 >>> bprint(_globre(br'*'))
713 [^/]*
712 [^/]*
714 >>> bprint(_globre(br'**'))
713 >>> bprint(_globre(br'**'))
715 .*
714 .*
716 >>> bprint(_globre(br'**/a'))
715 >>> bprint(_globre(br'**/a'))
717 (?:.*/)?a
716 (?:.*/)?a
718 >>> bprint(_globre(br'a/**/b'))
717 >>> bprint(_globre(br'a/**/b'))
719 a\/(?:.*/)?b
718 a\/(?:.*/)?b
720 >>> bprint(_globre(br'[a*?!^][^b][!c]'))
719 >>> bprint(_globre(br'[a*?!^][^b][!c]'))
721 [a*?!^][\^b][^c]
720 [a*?!^][\^b][^c]
722 >>> bprint(_globre(br'{a,b}'))
721 >>> bprint(_globre(br'{a,b}'))
723 (?:a|b)
722 (?:a|b)
724 >>> bprint(_globre(br'.\*\?'))
723 >>> bprint(_globre(br'.\*\?'))
725 \.\*\?
724 \.\*\?
726 '''
725 '''
727 i, n = 0, len(pat)
726 i, n = 0, len(pat)
728 res = ''
727 res = ''
729 group = 0
728 group = 0
730 escape = util.re.escape
729 escape = util.re.escape
731 def peek():
730 def peek():
732 return i < n and pat[i:i + 1]
731 return i < n and pat[i:i + 1]
733 while i < n:
732 while i < n:
734 c = pat[i:i + 1]
733 c = pat[i:i + 1]
735 i += 1
734 i += 1
736 if c not in '*?[{},\\':
735 if c not in '*?[{},\\':
737 res += escape(c)
736 res += escape(c)
738 elif c == '*':
737 elif c == '*':
739 if peek() == '*':
738 if peek() == '*':
740 i += 1
739 i += 1
741 if peek() == '/':
740 if peek() == '/':
742 i += 1
741 i += 1
743 res += '(?:.*/)?'
742 res += '(?:.*/)?'
744 else:
743 else:
745 res += '.*'
744 res += '.*'
746 else:
745 else:
747 res += '[^/]*'
746 res += '[^/]*'
748 elif c == '?':
747 elif c == '?':
749 res += '.'
748 res += '.'
750 elif c == '[':
749 elif c == '[':
751 j = i
750 j = i
752 if j < n and pat[j:j + 1] in '!]':
751 if j < n and pat[j:j + 1] in '!]':
753 j += 1
752 j += 1
754 while j < n and pat[j:j + 1] != ']':
753 while j < n and pat[j:j + 1] != ']':
755 j += 1
754 j += 1
756 if j >= n:
755 if j >= n:
757 res += '\\['
756 res += '\\['
758 else:
757 else:
759 stuff = pat[i:j].replace('\\','\\\\')
758 stuff = pat[i:j].replace('\\','\\\\')
760 i = j + 1
759 i = j + 1
761 if stuff[0:1] == '!':
760 if stuff[0:1] == '!':
762 stuff = '^' + stuff[1:]
761 stuff = '^' + stuff[1:]
763 elif stuff[0:1] == '^':
762 elif stuff[0:1] == '^':
764 stuff = '\\' + stuff
763 stuff = '\\' + stuff
765 res = '%s[%s]' % (res, stuff)
764 res = '%s[%s]' % (res, stuff)
766 elif c == '{':
765 elif c == '{':
767 group += 1
766 group += 1
768 res += '(?:'
767 res += '(?:'
769 elif c == '}' and group:
768 elif c == '}' and group:
770 res += ')'
769 res += ')'
771 group -= 1
770 group -= 1
772 elif c == ',' and group:
771 elif c == ',' and group:
773 res += '|'
772 res += '|'
774 elif c == '\\':
773 elif c == '\\':
775 p = peek()
774 p = peek()
776 if p:
775 if p:
777 i += 1
776 i += 1
778 res += escape(p)
777 res += escape(p)
779 else:
778 else:
780 res += escape(c)
779 res += escape(c)
781 else:
780 else:
782 res += escape(c)
781 res += escape(c)
783 return res
782 return res
784
783
785 def _regex(kind, pat, globsuffix):
784 def _regex(kind, pat, globsuffix):
786 '''Convert a (normalized) pattern of any kind into a regular expression.
785 '''Convert a (normalized) pattern of any kind into a regular expression.
787 globsuffix is appended to the regexp of globs.'''
786 globsuffix is appended to the regexp of globs.'''
788 if not pat:
787 if not pat:
789 return ''
788 return ''
790 if kind == 're':
789 if kind == 're':
791 return pat
790 return pat
792 if kind in ('path', 'relpath'):
791 if kind in ('path', 'relpath'):
793 if pat == '.':
792 if pat == '.':
794 return ''
793 return ''
795 return util.re.escape(pat) + '(?:/|$)'
794 return util.re.escape(pat) + '(?:/|$)'
796 if kind == 'rootfilesin':
795 if kind == 'rootfilesin':
797 if pat == '.':
796 if pat == '.':
798 escaped = ''
797 escaped = ''
799 else:
798 else:
800 # Pattern is a directory name.
799 # Pattern is a directory name.
801 escaped = util.re.escape(pat) + '/'
800 escaped = util.re.escape(pat) + '/'
802 # Anything after the pattern must be a non-directory.
801 # Anything after the pattern must be a non-directory.
803 return escaped + '[^/]+$'
802 return escaped + '[^/]+$'
804 if kind == 'relglob':
803 if kind == 'relglob':
805 return '(?:|.*/)' + _globre(pat) + globsuffix
804 return '(?:|.*/)' + _globre(pat) + globsuffix
806 if kind == 'relre':
805 if kind == 'relre':
807 if pat.startswith('^'):
806 if pat.startswith('^'):
808 return pat
807 return pat
809 return '.*' + pat
808 return '.*' + pat
810 return _globre(pat) + globsuffix
809 return _globre(pat) + globsuffix
811
810
812 def _buildmatch(ctx, kindpats, globsuffix, listsubrepos, root):
811 def _buildmatch(ctx, kindpats, globsuffix, listsubrepos, root):
813 '''Return regexp string and a matcher function for kindpats.
812 '''Return regexp string and a matcher function for kindpats.
814 globsuffix is appended to the regexp of globs.'''
813 globsuffix is appended to the regexp of globs.'''
815 matchfuncs = []
814 matchfuncs = []
816
815
817 subincludes, kindpats = _expandsubinclude(kindpats, root)
816 subincludes, kindpats = _expandsubinclude(kindpats, root)
818 if subincludes:
817 if subincludes:
819 submatchers = {}
818 submatchers = {}
820 def matchsubinclude(f):
819 def matchsubinclude(f):
821 for prefix, matcherargs in subincludes:
820 for prefix, matcherargs in subincludes:
822 if f.startswith(prefix):
821 if f.startswith(prefix):
823 mf = submatchers.get(prefix)
822 mf = submatchers.get(prefix)
824 if mf is None:
823 if mf is None:
825 mf = match(*matcherargs)
824 mf = match(*matcherargs)
826 submatchers[prefix] = mf
825 submatchers[prefix] = mf
827
826
828 if mf(f[len(prefix):]):
827 if mf(f[len(prefix):]):
829 return True
828 return True
830 return False
829 return False
831 matchfuncs.append(matchsubinclude)
830 matchfuncs.append(matchsubinclude)
832
831
833 fset, kindpats = _expandsets(kindpats, ctx, listsubrepos)
832 fset, kindpats = _expandsets(kindpats, ctx, listsubrepos)
834 if fset:
833 if fset:
835 matchfuncs.append(fset.__contains__)
834 matchfuncs.append(fset.__contains__)
836
835
837 regex = ''
836 regex = ''
838 if kindpats:
837 if kindpats:
839 regex, mf = _buildregexmatch(kindpats, globsuffix)
838 regex, mf = _buildregexmatch(kindpats, globsuffix)
840 matchfuncs.append(mf)
839 matchfuncs.append(mf)
841
840
842 if len(matchfuncs) == 1:
841 if len(matchfuncs) == 1:
843 return regex, matchfuncs[0]
842 return regex, matchfuncs[0]
844 else:
843 else:
845 return regex, lambda f: any(mf(f) for mf in matchfuncs)
844 return regex, lambda f: any(mf(f) for mf in matchfuncs)
846
845
847 def _buildregexmatch(kindpats, globsuffix):
846 def _buildregexmatch(kindpats, globsuffix):
848 """Build a match function from a list of kinds and kindpats,
847 """Build a match function from a list of kinds and kindpats,
849 return regexp string and a matcher function."""
848 return regexp string and a matcher function."""
850 try:
849 try:
851 regex = '(?:%s)' % '|'.join([_regex(k, p, globsuffix)
850 regex = '(?:%s)' % '|'.join([_regex(k, p, globsuffix)
852 for (k, p, s) in kindpats])
851 for (k, p, s) in kindpats])
853 if len(regex) > 20000:
852 if len(regex) > 20000:
854 raise OverflowError
853 raise OverflowError
855 return regex, _rematcher(regex)
854 return regex, _rematcher(regex)
856 except OverflowError:
855 except OverflowError:
857 # We're using a Python with a tiny regex engine and we
856 # We're using a Python with a tiny regex engine and we
858 # made it explode, so we'll divide the pattern list in two
857 # made it explode, so we'll divide the pattern list in two
859 # until it works
858 # until it works
860 l = len(kindpats)
859 l = len(kindpats)
861 if l < 2:
860 if l < 2:
862 raise
861 raise
863 regexa, a = _buildregexmatch(kindpats[:l//2], globsuffix)
862 regexa, a = _buildregexmatch(kindpats[:l//2], globsuffix)
864 regexb, b = _buildregexmatch(kindpats[l//2:], globsuffix)
863 regexb, b = _buildregexmatch(kindpats[l//2:], globsuffix)
865 return regex, lambda s: a(s) or b(s)
864 return regex, lambda s: a(s) or b(s)
866 except re.error:
865 except re.error:
867 for k, p, s in kindpats:
866 for k, p, s in kindpats:
868 try:
867 try:
869 _rematcher('(?:%s)' % _regex(k, p, globsuffix))
868 _rematcher('(?:%s)' % _regex(k, p, globsuffix))
870 except re.error:
869 except re.error:
871 if s:
870 if s:
872 raise error.Abort(_("%s: invalid pattern (%s): %s") %
871 raise error.Abort(_("%s: invalid pattern (%s): %s") %
873 (s, k, p))
872 (s, k, p))
874 else:
873 else:
875 raise error.Abort(_("invalid pattern (%s): %s") % (k, p))
874 raise error.Abort(_("invalid pattern (%s): %s") % (k, p))
876 raise error.Abort(_("invalid pattern"))
875 raise error.Abort(_("invalid pattern"))
877
876
878 def _patternrootsanddirs(kindpats):
877 def _patternrootsanddirs(kindpats):
879 '''Returns roots and directories corresponding to each pattern.
878 '''Returns roots and directories corresponding to each pattern.
880
879
881 This calculates the roots and directories exactly matching the patterns and
880 This calculates the roots and directories exactly matching the patterns and
882 returns a tuple of (roots, dirs) for each. It does not return other
881 returns a tuple of (roots, dirs) for each. It does not return other
883 directories which may also need to be considered, like the parent
882 directories which may also need to be considered, like the parent
884 directories.
883 directories.
885 '''
884 '''
886 r = []
885 r = []
887 d = []
886 d = []
888 for kind, pat, source in kindpats:
887 for kind, pat, source in kindpats:
889 if kind == 'glob': # find the non-glob prefix
888 if kind == 'glob': # find the non-glob prefix
890 root = []
889 root = []
891 for p in pat.split('/'):
890 for p in pat.split('/'):
892 if '[' in p or '{' in p or '*' in p or '?' in p:
891 if '[' in p or '{' in p or '*' in p or '?' in p:
893 break
892 break
894 root.append(p)
893 root.append(p)
895 r.append('/'.join(root) or '.')
894 r.append('/'.join(root) or '.')
896 elif kind in ('relpath', 'path'):
895 elif kind in ('relpath', 'path'):
897 r.append(pat or '.')
896 r.append(pat or '.')
898 elif kind in ('rootfilesin',):
897 elif kind in ('rootfilesin',):
899 d.append(pat or '.')
898 d.append(pat or '.')
900 else: # relglob, re, relre
899 else: # relglob, re, relre
901 r.append('.')
900 r.append('.')
902 return r, d
901 return r, d
903
902
904 def _roots(kindpats):
903 def _roots(kindpats):
905 '''Returns root directories to match recursively from the given patterns.'''
904 '''Returns root directories to match recursively from the given patterns.'''
906 roots, dirs = _patternrootsanddirs(kindpats)
905 roots, dirs = _patternrootsanddirs(kindpats)
907 return roots
906 return roots
908
907
909 def _rootsanddirs(kindpats):
908 def _rootsanddirs(kindpats):
910 '''Returns roots and exact directories from patterns.
909 '''Returns roots and exact directories from patterns.
911
910
912 roots are directories to match recursively, whereas exact directories should
911 roots are directories to match recursively, whereas exact directories should
913 be matched non-recursively. The returned (roots, dirs) tuple will also
912 be matched non-recursively. The returned (roots, dirs) tuple will also
914 include directories that need to be implicitly considered as either, such as
913 include directories that need to be implicitly considered as either, such as
915 parent directories.
914 parent directories.
916
915
917 >>> _rootsanddirs(
916 >>> _rootsanddirs(
918 ... [(b'glob', b'g/h/*', b''), (b'glob', b'g/h', b''),
917 ... [(b'glob', b'g/h/*', b''), (b'glob', b'g/h', b''),
919 ... (b'glob', b'g*', b'')])
918 ... (b'glob', b'g*', b'')])
920 (['g/h', 'g/h', '.'], ['g', '.'])
919 (['g/h', 'g/h', '.'], ['g', '.'])
921 >>> _rootsanddirs(
920 >>> _rootsanddirs(
922 ... [(b'rootfilesin', b'g/h', b''), (b'rootfilesin', b'', b'')])
921 ... [(b'rootfilesin', b'g/h', b''), (b'rootfilesin', b'', b'')])
923 ([], ['g/h', '.', 'g', '.'])
922 ([], ['g/h', '.', 'g', '.'])
924 >>> _rootsanddirs(
923 >>> _rootsanddirs(
925 ... [(b'relpath', b'r', b''), (b'path', b'p/p', b''),
924 ... [(b'relpath', b'r', b''), (b'path', b'p/p', b''),
926 ... (b'path', b'', b'')])
925 ... (b'path', b'', b'')])
927 (['r', 'p/p', '.'], ['p', '.'])
926 (['r', 'p/p', '.'], ['p', '.'])
928 >>> _rootsanddirs(
927 >>> _rootsanddirs(
929 ... [(b'relglob', b'rg*', b''), (b're', b're/', b''),
928 ... [(b'relglob', b'rg*', b''), (b're', b're/', b''),
930 ... (b'relre', b'rr', b'')])
929 ... (b'relre', b'rr', b'')])
931 (['.', '.', '.'], ['.'])
930 (['.', '.', '.'], ['.'])
932 '''
931 '''
933 r, d = _patternrootsanddirs(kindpats)
932 r, d = _patternrootsanddirs(kindpats)
934
933
935 # Append the parents as non-recursive/exact directories, since they must be
934 # Append the parents as non-recursive/exact directories, since they must be
936 # scanned to get to either the roots or the other exact directories.
935 # scanned to get to either the roots or the other exact directories.
937 d.extend(util.dirs(d))
936 d.extend(util.dirs(d))
938 d.extend(util.dirs(r))
937 d.extend(util.dirs(r))
939 # util.dirs() does not include the root directory, so add it manually
938 # util.dirs() does not include the root directory, so add it manually
940 d.append('.')
939 d.append('.')
941
940
942 return r, d
941 return r, d
943
942
944 def _explicitfiles(kindpats):
943 def _explicitfiles(kindpats):
945 '''Returns the potential explicit filenames from the patterns.
944 '''Returns the potential explicit filenames from the patterns.
946
945
947 >>> _explicitfiles([(b'path', b'foo/bar', b'')])
946 >>> _explicitfiles([(b'path', b'foo/bar', b'')])
948 ['foo/bar']
947 ['foo/bar']
949 >>> _explicitfiles([(b'rootfilesin', b'foo/bar', b'')])
948 >>> _explicitfiles([(b'rootfilesin', b'foo/bar', b'')])
950 []
949 []
951 '''
950 '''
952 # Keep only the pattern kinds where one can specify filenames (vs only
951 # Keep only the pattern kinds where one can specify filenames (vs only
953 # directory names).
952 # directory names).
954 filable = [kp for kp in kindpats if kp[0] not in ('rootfilesin',)]
953 filable = [kp for kp in kindpats if kp[0] not in ('rootfilesin',)]
955 return _roots(filable)
954 return _roots(filable)
956
955
957 def _prefix(kindpats):
956 def _prefix(kindpats):
958 '''Whether all the patterns match a prefix (i.e. recursively)'''
957 '''Whether all the patterns match a prefix (i.e. recursively)'''
959 for kind, pat, source in kindpats:
958 for kind, pat, source in kindpats:
960 if kind not in ('path', 'relpath'):
959 if kind not in ('path', 'relpath'):
961 return False
960 return False
962 return True
961 return True
963
962
964 _commentre = None
963 _commentre = None
965
964
966 def readpatternfile(filepath, warn, sourceinfo=False):
965 def readpatternfile(filepath, warn, sourceinfo=False):
967 '''parse a pattern file, returning a list of
966 '''parse a pattern file, returning a list of
968 patterns. These patterns should be given to compile()
967 patterns. These patterns should be given to compile()
969 to be validated and converted into a match function.
968 to be validated and converted into a match function.
970
969
971 trailing white space is dropped.
970 trailing white space is dropped.
972 the escape character is backslash.
971 the escape character is backslash.
973 comments start with #.
972 comments start with #.
974 empty lines are skipped.
973 empty lines are skipped.
975
974
976 lines can be of the following formats:
975 lines can be of the following formats:
977
976
978 syntax: regexp # defaults following lines to non-rooted regexps
977 syntax: regexp # defaults following lines to non-rooted regexps
979 syntax: glob # defaults following lines to non-rooted globs
978 syntax: glob # defaults following lines to non-rooted globs
980 re:pattern # non-rooted regular expression
979 re:pattern # non-rooted regular expression
981 glob:pattern # non-rooted glob
980 glob:pattern # non-rooted glob
982 pattern # pattern of the current default type
981 pattern # pattern of the current default type
983
982
984 if sourceinfo is set, returns a list of tuples:
983 if sourceinfo is set, returns a list of tuples:
985 (pattern, lineno, originalline). This is useful to debug ignore patterns.
984 (pattern, lineno, originalline). This is useful to debug ignore patterns.
986 '''
985 '''
987
986
988 syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:',
987 syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:',
989 'include': 'include', 'subinclude': 'subinclude'}
988 'include': 'include', 'subinclude': 'subinclude'}
990 syntax = 'relre:'
989 syntax = 'relre:'
991 patterns = []
990 patterns = []
992
991
993 fp = open(filepath, 'rb')
992 fp = open(filepath, 'rb')
994 for lineno, line in enumerate(util.iterfile(fp), start=1):
993 for lineno, line in enumerate(util.iterfile(fp), start=1):
995 if "#" in line:
994 if "#" in line:
996 global _commentre
995 global _commentre
997 if not _commentre:
996 if not _commentre:
998 _commentre = util.re.compile(br'((?:^|[^\\])(?:\\\\)*)#.*')
997 _commentre = util.re.compile(br'((?:^|[^\\])(?:\\\\)*)#.*')
999 # remove comments prefixed by an even number of escapes
998 # remove comments prefixed by an even number of escapes
1000 m = _commentre.search(line)
999 m = _commentre.search(line)
1001 if m:
1000 if m:
1002 line = line[:m.end(1)]
1001 line = line[:m.end(1)]
1003 # fixup properly escaped comments that survived the above
1002 # fixup properly escaped comments that survived the above
1004 line = line.replace("\\#", "#")
1003 line = line.replace("\\#", "#")
1005 line = line.rstrip()
1004 line = line.rstrip()
1006 if not line:
1005 if not line:
1007 continue
1006 continue
1008
1007
1009 if line.startswith('syntax:'):
1008 if line.startswith('syntax:'):
1010 s = line[7:].strip()
1009 s = line[7:].strip()
1011 try:
1010 try:
1012 syntax = syntaxes[s]
1011 syntax = syntaxes[s]
1013 except KeyError:
1012 except KeyError:
1014 if warn:
1013 if warn:
1015 warn(_("%s: ignoring invalid syntax '%s'\n") %
1014 warn(_("%s: ignoring invalid syntax '%s'\n") %
1016 (filepath, s))
1015 (filepath, s))
1017 continue
1016 continue
1018
1017
1019 linesyntax = syntax
1018 linesyntax = syntax
1020 for s, rels in syntaxes.iteritems():
1019 for s, rels in syntaxes.iteritems():
1021 if line.startswith(rels):
1020 if line.startswith(rels):
1022 linesyntax = rels
1021 linesyntax = rels
1023 line = line[len(rels):]
1022 line = line[len(rels):]
1024 break
1023 break
1025 elif line.startswith(s+':'):
1024 elif line.startswith(s+':'):
1026 linesyntax = rels
1025 linesyntax = rels
1027 line = line[len(s) + 1:]
1026 line = line[len(s) + 1:]
1028 break
1027 break
1029 if sourceinfo:
1028 if sourceinfo:
1030 patterns.append((linesyntax + line, lineno, line))
1029 patterns.append((linesyntax + line, lineno, line))
1031 else:
1030 else:
1032 patterns.append(linesyntax + line)
1031 patterns.append(linesyntax + line)
1033 fp.close()
1032 fp.close()
1034 return patterns
1033 return patterns
General Comments 0
You need to be logged in to leave comments. Login now