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