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