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