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