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