##// END OF EJS Templates
match: delete unused argument "listsubrepos" from _buildmatch()...
Martin von Zweigbergk -
r41818:a1326852 default
parent child Browse files
Show More
@@ -1,1377 +1,1374
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 itertools
11 import itertools
12 import os
12 import os
13 import re
13 import re
14
14
15 from .i18n import _
15 from .i18n import _
16 from . import (
16 from . import (
17 encoding,
17 encoding,
18 error,
18 error,
19 pathutil,
19 pathutil,
20 pycompat,
20 pycompat,
21 util,
21 util,
22 )
22 )
23 from .utils import (
23 from .utils import (
24 stringutil,
24 stringutil,
25 )
25 )
26
26
27 allpatternkinds = ('re', 'glob', 'path', 'relglob', 'relpath', 'relre',
27 allpatternkinds = ('re', 'glob', 'path', 'relglob', 'relpath', 'relre',
28 'rootglob',
28 'rootglob',
29 'listfile', 'listfile0', 'set', 'include', 'subinclude',
29 'listfile', 'listfile0', 'set', 'include', 'subinclude',
30 'rootfilesin')
30 'rootfilesin')
31 cwdrelativepatternkinds = ('relpath', 'glob')
31 cwdrelativepatternkinds = ('relpath', 'glob')
32
32
33 propertycache = util.propertycache
33 propertycache = util.propertycache
34
34
35 def _rematcher(regex):
35 def _rematcher(regex):
36 '''compile the regexp with the best available regexp engine and return a
36 '''compile the regexp with the best available regexp engine and return a
37 matcher function'''
37 matcher function'''
38 m = util.re.compile(regex)
38 m = util.re.compile(regex)
39 try:
39 try:
40 # slightly faster, provided by facebook's re2 bindings
40 # slightly faster, provided by facebook's re2 bindings
41 return m.test_match
41 return m.test_match
42 except AttributeError:
42 except AttributeError:
43 return m.match
43 return m.match
44
44
45 def _expandsets(root, cwd, kindpats, ctx, listsubrepos, badfn):
45 def _expandsets(root, cwd, kindpats, ctx, listsubrepos, badfn):
46 '''Returns the kindpats list with the 'set' patterns expanded to matchers'''
46 '''Returns the kindpats list with the 'set' patterns expanded to matchers'''
47 matchers = []
47 matchers = []
48 other = []
48 other = []
49
49
50 for kind, pat, source in kindpats:
50 for kind, pat, source in kindpats:
51 if kind == 'set':
51 if kind == 'set':
52 if ctx is None:
52 if ctx is None:
53 raise error.ProgrammingError("fileset expression with no "
53 raise error.ProgrammingError("fileset expression with no "
54 "context")
54 "context")
55 matchers.append(ctx.matchfileset(pat, badfn=badfn))
55 matchers.append(ctx.matchfileset(pat, badfn=badfn))
56
56
57 if listsubrepos:
57 if listsubrepos:
58 for subpath in ctx.substate:
58 for subpath in ctx.substate:
59 sm = ctx.sub(subpath).matchfileset(pat, badfn=badfn)
59 sm = ctx.sub(subpath).matchfileset(pat, badfn=badfn)
60 pm = prefixdirmatcher(root, cwd, subpath, sm, badfn=badfn)
60 pm = prefixdirmatcher(root, cwd, subpath, sm, badfn=badfn)
61 matchers.append(pm)
61 matchers.append(pm)
62
62
63 continue
63 continue
64 other.append((kind, pat, source))
64 other.append((kind, pat, source))
65 return matchers, other
65 return matchers, other
66
66
67 def _expandsubinclude(kindpats, root):
67 def _expandsubinclude(kindpats, root):
68 '''Returns the list of subinclude matcher args and the kindpats without the
68 '''Returns the list of subinclude matcher args and the kindpats without the
69 subincludes in it.'''
69 subincludes in it.'''
70 relmatchers = []
70 relmatchers = []
71 other = []
71 other = []
72
72
73 for kind, pat, source in kindpats:
73 for kind, pat, source in kindpats:
74 if kind == 'subinclude':
74 if kind == 'subinclude':
75 sourceroot = pathutil.dirname(util.normpath(source))
75 sourceroot = pathutil.dirname(util.normpath(source))
76 pat = util.pconvert(pat)
76 pat = util.pconvert(pat)
77 path = pathutil.join(sourceroot, pat)
77 path = pathutil.join(sourceroot, pat)
78
78
79 newroot = pathutil.dirname(path)
79 newroot = pathutil.dirname(path)
80 matcherargs = (newroot, '', [], ['include:%s' % path])
80 matcherargs = (newroot, '', [], ['include:%s' % path])
81
81
82 prefix = pathutil.canonpath(root, root, newroot)
82 prefix = pathutil.canonpath(root, root, newroot)
83 if prefix:
83 if prefix:
84 prefix += '/'
84 prefix += '/'
85 relmatchers.append((prefix, matcherargs))
85 relmatchers.append((prefix, matcherargs))
86 else:
86 else:
87 other.append((kind, pat, source))
87 other.append((kind, pat, source))
88
88
89 return relmatchers, other
89 return relmatchers, other
90
90
91 def _kindpatsalwaysmatch(kindpats):
91 def _kindpatsalwaysmatch(kindpats):
92 """"Checks whether the kindspats match everything, as e.g.
92 """"Checks whether the kindspats match everything, as e.g.
93 'relpath:.' does.
93 'relpath:.' does.
94 """
94 """
95 for kind, pat, source in kindpats:
95 for kind, pat, source in kindpats:
96 if pat != '' or kind not in ['relpath', 'glob']:
96 if pat != '' or kind not in ['relpath', 'glob']:
97 return False
97 return False
98 return True
98 return True
99
99
100 def _buildkindpatsmatcher(matchercls, root, cwd, kindpats, ctx=None,
100 def _buildkindpatsmatcher(matchercls, root, cwd, kindpats, ctx=None,
101 listsubrepos=False, badfn=None):
101 listsubrepos=False, badfn=None):
102 matchers = []
102 matchers = []
103 fms, kindpats = _expandsets(root, cwd, kindpats, ctx=ctx,
103 fms, kindpats = _expandsets(root, cwd, kindpats, ctx=ctx,
104 listsubrepos=listsubrepos, badfn=badfn)
104 listsubrepos=listsubrepos, badfn=badfn)
105 if kindpats:
105 if kindpats:
106 m = matchercls(root, cwd, kindpats, listsubrepos=listsubrepos,
106 m = matchercls(root, cwd, kindpats, badfn=badfn)
107 badfn=badfn)
108 matchers.append(m)
107 matchers.append(m)
109 if fms:
108 if fms:
110 matchers.extend(fms)
109 matchers.extend(fms)
111 if not matchers:
110 if not matchers:
112 return nevermatcher(root, cwd, badfn=badfn)
111 return nevermatcher(root, cwd, badfn=badfn)
113 if len(matchers) == 1:
112 if len(matchers) == 1:
114 return matchers[0]
113 return matchers[0]
115 return unionmatcher(matchers)
114 return unionmatcher(matchers)
116
115
117 def match(root, cwd, patterns=None, include=None, exclude=None, default='glob',
116 def match(root, cwd, patterns=None, include=None, exclude=None, default='glob',
118 auditor=None, ctx=None, listsubrepos=False, warn=None,
117 auditor=None, ctx=None, listsubrepos=False, warn=None,
119 badfn=None, icasefs=False):
118 badfn=None, icasefs=False):
120 """build an object to match a set of file patterns
119 """build an object to match a set of file patterns
121
120
122 arguments:
121 arguments:
123 root - the canonical root of the tree you're matching against
122 root - the canonical root of the tree you're matching against
124 cwd - the current working directory, if relevant
123 cwd - the current working directory, if relevant
125 patterns - patterns to find
124 patterns - patterns to find
126 include - patterns to include (unless they are excluded)
125 include - patterns to include (unless they are excluded)
127 exclude - patterns to exclude (even if they are included)
126 exclude - patterns to exclude (even if they are included)
128 default - if a pattern in patterns has no explicit type, assume this one
127 default - if a pattern in patterns has no explicit type, assume this one
129 warn - optional function used for printing warnings
128 warn - optional function used for printing warnings
130 badfn - optional bad() callback for this matcher instead of the default
129 badfn - optional bad() callback for this matcher instead of the default
131 icasefs - make a matcher for wdir on case insensitive filesystems, which
130 icasefs - make a matcher for wdir on case insensitive filesystems, which
132 normalizes the given patterns to the case in the filesystem
131 normalizes the given patterns to the case in the filesystem
133
132
134 a pattern is one of:
133 a pattern is one of:
135 'glob:<glob>' - a glob relative to cwd
134 'glob:<glob>' - a glob relative to cwd
136 're:<regexp>' - a regular expression
135 're:<regexp>' - a regular expression
137 'path:<path>' - a path relative to repository root, which is matched
136 'path:<path>' - a path relative to repository root, which is matched
138 recursively
137 recursively
139 'rootfilesin:<path>' - a path relative to repository root, which is
138 'rootfilesin:<path>' - a path relative to repository root, which is
140 matched non-recursively (will not match subdirectories)
139 matched non-recursively (will not match subdirectories)
141 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
140 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
142 'relpath:<path>' - a path relative to cwd
141 'relpath:<path>' - a path relative to cwd
143 'relre:<regexp>' - a regexp that needn't match the start of a name
142 'relre:<regexp>' - a regexp that needn't match the start of a name
144 'set:<fileset>' - a fileset expression
143 'set:<fileset>' - a fileset expression
145 'include:<path>' - a file of patterns to read and include
144 'include:<path>' - a file of patterns to read and include
146 'subinclude:<path>' - a file of patterns to match against files under
145 'subinclude:<path>' - a file of patterns to match against files under
147 the same directory
146 the same directory
148 '<something>' - a pattern of the specified default type
147 '<something>' - a pattern of the specified default type
149 """
148 """
150 normalize = _donormalize
149 normalize = _donormalize
151 if icasefs:
150 if icasefs:
152 dirstate = ctx.repo().dirstate
151 dirstate = ctx.repo().dirstate
153 dsnormalize = dirstate.normalize
152 dsnormalize = dirstate.normalize
154
153
155 def normalize(patterns, default, root, cwd, auditor, warn):
154 def normalize(patterns, default, root, cwd, auditor, warn):
156 kp = _donormalize(patterns, default, root, cwd, auditor, warn)
155 kp = _donormalize(patterns, default, root, cwd, auditor, warn)
157 kindpats = []
156 kindpats = []
158 for kind, pats, source in kp:
157 for kind, pats, source in kp:
159 if kind not in ('re', 'relre'): # regex can't be normalized
158 if kind not in ('re', 'relre'): # regex can't be normalized
160 p = pats
159 p = pats
161 pats = dsnormalize(pats)
160 pats = dsnormalize(pats)
162
161
163 # Preserve the original to handle a case only rename.
162 # Preserve the original to handle a case only rename.
164 if p != pats and p in dirstate:
163 if p != pats and p in dirstate:
165 kindpats.append((kind, p, source))
164 kindpats.append((kind, p, source))
166
165
167 kindpats.append((kind, pats, source))
166 kindpats.append((kind, pats, source))
168 return kindpats
167 return kindpats
169
168
170 if patterns:
169 if patterns:
171 kindpats = normalize(patterns, default, root, cwd, auditor, warn)
170 kindpats = normalize(patterns, default, root, cwd, auditor, warn)
172 if _kindpatsalwaysmatch(kindpats):
171 if _kindpatsalwaysmatch(kindpats):
173 m = alwaysmatcher(root, cwd, badfn)
172 m = alwaysmatcher(root, cwd, badfn)
174 else:
173 else:
175 m = _buildkindpatsmatcher(patternmatcher, root, cwd, kindpats,
174 m = _buildkindpatsmatcher(patternmatcher, root, cwd, kindpats,
176 ctx=ctx, listsubrepos=listsubrepos,
175 ctx=ctx, listsubrepos=listsubrepos,
177 badfn=badfn)
176 badfn=badfn)
178 else:
177 else:
179 # It's a little strange that no patterns means to match everything.
178 # It's a little strange that no patterns means to match everything.
180 # Consider changing this to match nothing (probably using nevermatcher).
179 # Consider changing this to match nothing (probably using nevermatcher).
181 m = alwaysmatcher(root, cwd, badfn)
180 m = alwaysmatcher(root, cwd, badfn)
182
181
183 if include:
182 if include:
184 kindpats = normalize(include, 'glob', root, cwd, auditor, warn)
183 kindpats = normalize(include, 'glob', root, cwd, auditor, warn)
185 im = _buildkindpatsmatcher(includematcher, root, cwd, kindpats, ctx=ctx,
184 im = _buildkindpatsmatcher(includematcher, root, cwd, kindpats, ctx=ctx,
186 listsubrepos=listsubrepos, badfn=None)
185 listsubrepos=listsubrepos, badfn=None)
187 m = intersectmatchers(m, im)
186 m = intersectmatchers(m, im)
188 if exclude:
187 if exclude:
189 kindpats = normalize(exclude, 'glob', root, cwd, auditor, warn)
188 kindpats = normalize(exclude, 'glob', root, cwd, auditor, warn)
190 em = _buildkindpatsmatcher(includematcher, root, cwd, kindpats, ctx=ctx,
189 em = _buildkindpatsmatcher(includematcher, root, cwd, kindpats, ctx=ctx,
191 listsubrepos=listsubrepos, badfn=None)
190 listsubrepos=listsubrepos, badfn=None)
192 m = differencematcher(m, em)
191 m = differencematcher(m, em)
193 return m
192 return m
194
193
195 def exact(root, cwd, files, badfn=None):
194 def exact(root, cwd, files, badfn=None):
196 return exactmatcher(root, cwd, files, badfn=badfn)
195 return exactmatcher(root, cwd, files, badfn=badfn)
197
196
198 def always(root, cwd):
197 def always(root, cwd):
199 return alwaysmatcher(root, cwd)
198 return alwaysmatcher(root, cwd)
200
199
201 def never(root, cwd):
200 def never(root, cwd):
202 return nevermatcher(root, cwd)
201 return nevermatcher(root, cwd)
203
202
204 def badmatch(match, badfn):
203 def badmatch(match, badfn):
205 """Make a copy of the given matcher, replacing its bad method with the given
204 """Make a copy of the given matcher, replacing its bad method with the given
206 one.
205 one.
207 """
206 """
208 m = copy.copy(match)
207 m = copy.copy(match)
209 m.bad = badfn
208 m.bad = badfn
210 return m
209 return m
211
210
212 def _donormalize(patterns, default, root, cwd, auditor, warn):
211 def _donormalize(patterns, default, root, cwd, auditor, warn):
213 '''Convert 'kind:pat' from the patterns list to tuples with kind and
212 '''Convert 'kind:pat' from the patterns list to tuples with kind and
214 normalized and rooted patterns and with listfiles expanded.'''
213 normalized and rooted patterns and with listfiles expanded.'''
215 kindpats = []
214 kindpats = []
216 for kind, pat in [_patsplit(p, default) for p in patterns]:
215 for kind, pat in [_patsplit(p, default) for p in patterns]:
217 if kind in cwdrelativepatternkinds:
216 if kind in cwdrelativepatternkinds:
218 pat = pathutil.canonpath(root, cwd, pat, auditor)
217 pat = pathutil.canonpath(root, cwd, pat, auditor)
219 elif kind in ('relglob', 'path', 'rootfilesin', 'rootglob'):
218 elif kind in ('relglob', 'path', 'rootfilesin', 'rootglob'):
220 pat = util.normpath(pat)
219 pat = util.normpath(pat)
221 elif kind in ('listfile', 'listfile0'):
220 elif kind in ('listfile', 'listfile0'):
222 try:
221 try:
223 files = util.readfile(pat)
222 files = util.readfile(pat)
224 if kind == 'listfile0':
223 if kind == 'listfile0':
225 files = files.split('\0')
224 files = files.split('\0')
226 else:
225 else:
227 files = files.splitlines()
226 files = files.splitlines()
228 files = [f for f in files if f]
227 files = [f for f in files if f]
229 except EnvironmentError:
228 except EnvironmentError:
230 raise error.Abort(_("unable to read file list (%s)") % pat)
229 raise error.Abort(_("unable to read file list (%s)") % pat)
231 for k, p, source in _donormalize(files, default, root, cwd,
230 for k, p, source in _donormalize(files, default, root, cwd,
232 auditor, warn):
231 auditor, warn):
233 kindpats.append((k, p, pat))
232 kindpats.append((k, p, pat))
234 continue
233 continue
235 elif kind == 'include':
234 elif kind == 'include':
236 try:
235 try:
237 fullpath = os.path.join(root, util.localpath(pat))
236 fullpath = os.path.join(root, util.localpath(pat))
238 includepats = readpatternfile(fullpath, warn)
237 includepats = readpatternfile(fullpath, warn)
239 for k, p, source in _donormalize(includepats, default,
238 for k, p, source in _donormalize(includepats, default,
240 root, cwd, auditor, warn):
239 root, cwd, auditor, warn):
241 kindpats.append((k, p, source or pat))
240 kindpats.append((k, p, source or pat))
242 except error.Abort as inst:
241 except error.Abort as inst:
243 raise error.Abort('%s: %s' % (pat, inst[0]))
242 raise error.Abort('%s: %s' % (pat, inst[0]))
244 except IOError as inst:
243 except IOError as inst:
245 if warn:
244 if warn:
246 warn(_("skipping unreadable pattern file '%s': %s\n") %
245 warn(_("skipping unreadable pattern file '%s': %s\n") %
247 (pat, stringutil.forcebytestr(inst.strerror)))
246 (pat, stringutil.forcebytestr(inst.strerror)))
248 continue
247 continue
249 # else: re or relre - which cannot be normalized
248 # else: re or relre - which cannot be normalized
250 kindpats.append((kind, pat, ''))
249 kindpats.append((kind, pat, ''))
251 return kindpats
250 return kindpats
252
251
253 class basematcher(object):
252 class basematcher(object):
254
253
255 def __init__(self, root, cwd, badfn=None):
254 def __init__(self, root, cwd, badfn=None):
256 self._root = root
255 self._root = root
257 self._cwd = cwd
256 self._cwd = cwd
258 if badfn is not None:
257 if badfn is not None:
259 self.bad = badfn
258 self.bad = badfn
260
259
261 def __call__(self, fn):
260 def __call__(self, fn):
262 return self.matchfn(fn)
261 return self.matchfn(fn)
263 def __iter__(self):
262 def __iter__(self):
264 for f in self._files:
263 for f in self._files:
265 yield f
264 yield f
266 # Callbacks related to how the matcher is used by dirstate.walk.
265 # Callbacks related to how the matcher is used by dirstate.walk.
267 # Subscribers to these events must monkeypatch the matcher object.
266 # Subscribers to these events must monkeypatch the matcher object.
268 def bad(self, f, msg):
267 def bad(self, f, msg):
269 '''Callback from dirstate.walk for each explicit file that can't be
268 '''Callback from dirstate.walk for each explicit file that can't be
270 found/accessed, with an error message.'''
269 found/accessed, with an error message.'''
271
270
272 # If an explicitdir is set, it will be called when an explicitly listed
271 # If an explicitdir is set, it will be called when an explicitly listed
273 # directory is visited.
272 # directory is visited.
274 explicitdir = None
273 explicitdir = None
275
274
276 # If an traversedir is set, it will be called when a directory discovered
275 # If an traversedir is set, it will be called when a directory discovered
277 # by recursive traversal is visited.
276 # by recursive traversal is visited.
278 traversedir = None
277 traversedir = None
279
278
280 @propertycache
279 @propertycache
281 def _files(self):
280 def _files(self):
282 return []
281 return []
283
282
284 def files(self):
283 def files(self):
285 '''Explicitly listed files or patterns or roots:
284 '''Explicitly listed files or patterns or roots:
286 if no patterns or .always(): empty list,
285 if no patterns or .always(): empty list,
287 if exact: list exact files,
286 if exact: list exact files,
288 if not .anypats(): list all files and dirs,
287 if not .anypats(): list all files and dirs,
289 else: optimal roots'''
288 else: optimal roots'''
290 return self._files
289 return self._files
291
290
292 @propertycache
291 @propertycache
293 def _fileset(self):
292 def _fileset(self):
294 return set(self._files)
293 return set(self._files)
295
294
296 def exact(self, f):
295 def exact(self, f):
297 '''Returns True if f is in .files().'''
296 '''Returns True if f is in .files().'''
298 return f in self._fileset
297 return f in self._fileset
299
298
300 def matchfn(self, f):
299 def matchfn(self, f):
301 return False
300 return False
302
301
303 def visitdir(self, dir):
302 def visitdir(self, dir):
304 '''Decides whether a directory should be visited based on whether it
303 '''Decides whether a directory should be visited based on whether it
305 has potential matches in it or one of its subdirectories. This is
304 has potential matches in it or one of its subdirectories. This is
306 based on the match's primary, included, and excluded patterns.
305 based on the match's primary, included, and excluded patterns.
307
306
308 Returns the string 'all' if the given directory and all subdirectories
307 Returns the string 'all' if the given directory and all subdirectories
309 should be visited. Otherwise returns True or False indicating whether
308 should be visited. Otherwise returns True or False indicating whether
310 the given directory should be visited.
309 the given directory should be visited.
311 '''
310 '''
312 return True
311 return True
313
312
314 def visitchildrenset(self, dir):
313 def visitchildrenset(self, dir):
315 '''Decides whether a directory should be visited based on whether it
314 '''Decides whether a directory should be visited based on whether it
316 has potential matches in it or one of its subdirectories, and
315 has potential matches in it or one of its subdirectories, and
317 potentially lists which subdirectories of that directory should be
316 potentially lists which subdirectories of that directory should be
318 visited. This is based on the match's primary, included, and excluded
317 visited. This is based on the match's primary, included, and excluded
319 patterns.
318 patterns.
320
319
321 This function is very similar to 'visitdir', and the following mapping
320 This function is very similar to 'visitdir', and the following mapping
322 can be applied:
321 can be applied:
323
322
324 visitdir | visitchildrenlist
323 visitdir | visitchildrenlist
325 ----------+-------------------
324 ----------+-------------------
326 False | set()
325 False | set()
327 'all' | 'all'
326 'all' | 'all'
328 True | 'this' OR non-empty set of subdirs -or files- to visit
327 True | 'this' OR non-empty set of subdirs -or files- to visit
329
328
330 Example:
329 Example:
331 Assume matchers ['path:foo/bar', 'rootfilesin:qux'], we would return
330 Assume matchers ['path:foo/bar', 'rootfilesin:qux'], we would return
332 the following values (assuming the implementation of visitchildrenset
331 the following values (assuming the implementation of visitchildrenset
333 is capable of recognizing this; some implementations are not).
332 is capable of recognizing this; some implementations are not).
334
333
335 '.' -> {'foo', 'qux'}
334 '.' -> {'foo', 'qux'}
336 'baz' -> set()
335 'baz' -> set()
337 'foo' -> {'bar'}
336 'foo' -> {'bar'}
338 # Ideally this would be 'all', but since the prefix nature of matchers
337 # Ideally this would be 'all', but since the prefix nature of matchers
339 # is applied to the entire matcher, we have to downgrade this to
338 # is applied to the entire matcher, we have to downgrade this to
340 # 'this' due to the non-prefix 'rootfilesin'-kind matcher being mixed
339 # 'this' due to the non-prefix 'rootfilesin'-kind matcher being mixed
341 # in.
340 # in.
342 'foo/bar' -> 'this'
341 'foo/bar' -> 'this'
343 'qux' -> 'this'
342 'qux' -> 'this'
344
343
345 Important:
344 Important:
346 Most matchers do not know if they're representing files or
345 Most matchers do not know if they're representing files or
347 directories. They see ['path:dir/f'] and don't know whether 'f' is a
346 directories. They see ['path:dir/f'] and don't know whether 'f' is a
348 file or a directory, so visitchildrenset('dir') for most matchers will
347 file or a directory, so visitchildrenset('dir') for most matchers will
349 return {'f'}, but if the matcher knows it's a file (like exactmatcher
348 return {'f'}, but if the matcher knows it's a file (like exactmatcher
350 does), it may return 'this'. Do not rely on the return being a set
349 does), it may return 'this'. Do not rely on the return being a set
351 indicating that there are no files in this dir to investigate (or
350 indicating that there are no files in this dir to investigate (or
352 equivalently that if there are files to investigate in 'dir' that it
351 equivalently that if there are files to investigate in 'dir' that it
353 will always return 'this').
352 will always return 'this').
354 '''
353 '''
355 return 'this'
354 return 'this'
356
355
357 def always(self):
356 def always(self):
358 '''Matcher will match everything and .files() will be empty --
357 '''Matcher will match everything and .files() will be empty --
359 optimization might be possible.'''
358 optimization might be possible.'''
360 return False
359 return False
361
360
362 def isexact(self):
361 def isexact(self):
363 '''Matcher will match exactly the list of files in .files() --
362 '''Matcher will match exactly the list of files in .files() --
364 optimization might be possible.'''
363 optimization might be possible.'''
365 return False
364 return False
366
365
367 def prefix(self):
366 def prefix(self):
368 '''Matcher will match the paths in .files() recursively --
367 '''Matcher will match the paths in .files() recursively --
369 optimization might be possible.'''
368 optimization might be possible.'''
370 return False
369 return False
371
370
372 def anypats(self):
371 def anypats(self):
373 '''None of .always(), .isexact(), and .prefix() is true --
372 '''None of .always(), .isexact(), and .prefix() is true --
374 optimizations will be difficult.'''
373 optimizations will be difficult.'''
375 return not self.always() and not self.isexact() and not self.prefix()
374 return not self.always() and not self.isexact() and not self.prefix()
376
375
377 class alwaysmatcher(basematcher):
376 class alwaysmatcher(basematcher):
378 '''Matches everything.'''
377 '''Matches everything.'''
379
378
380 def __init__(self, root, cwd, badfn=None):
379 def __init__(self, root, cwd, badfn=None):
381 super(alwaysmatcher, self).__init__(root, cwd, badfn)
380 super(alwaysmatcher, self).__init__(root, cwd, badfn)
382
381
383 def always(self):
382 def always(self):
384 return True
383 return True
385
384
386 def matchfn(self, f):
385 def matchfn(self, f):
387 return True
386 return True
388
387
389 def visitdir(self, dir):
388 def visitdir(self, dir):
390 return 'all'
389 return 'all'
391
390
392 def visitchildrenset(self, dir):
391 def visitchildrenset(self, dir):
393 return 'all'
392 return 'all'
394
393
395 def __repr__(self):
394 def __repr__(self):
396 return r'<alwaysmatcher>'
395 return r'<alwaysmatcher>'
397
396
398 class nevermatcher(basematcher):
397 class nevermatcher(basematcher):
399 '''Matches nothing.'''
398 '''Matches nothing.'''
400
399
401 def __init__(self, root, cwd, badfn=None):
400 def __init__(self, root, cwd, badfn=None):
402 super(nevermatcher, self).__init__(root, cwd, badfn)
401 super(nevermatcher, self).__init__(root, cwd, badfn)
403
402
404 # It's a little weird to say that the nevermatcher is an exact matcher
403 # It's a little weird to say that the nevermatcher is an exact matcher
405 # or a prefix matcher, but it seems to make sense to let callers take
404 # or a prefix matcher, but it seems to make sense to let callers take
406 # fast paths based on either. There will be no exact matches, nor any
405 # fast paths based on either. There will be no exact matches, nor any
407 # prefixes (files() returns []), so fast paths iterating over them should
406 # prefixes (files() returns []), so fast paths iterating over them should
408 # be efficient (and correct).
407 # be efficient (and correct).
409 def isexact(self):
408 def isexact(self):
410 return True
409 return True
411
410
412 def prefix(self):
411 def prefix(self):
413 return True
412 return True
414
413
415 def visitdir(self, dir):
414 def visitdir(self, dir):
416 return False
415 return False
417
416
418 def visitchildrenset(self, dir):
417 def visitchildrenset(self, dir):
419 return set()
418 return set()
420
419
421 def __repr__(self):
420 def __repr__(self):
422 return r'<nevermatcher>'
421 return r'<nevermatcher>'
423
422
424 class predicatematcher(basematcher):
423 class predicatematcher(basematcher):
425 """A matcher adapter for a simple boolean function"""
424 """A matcher adapter for a simple boolean function"""
426
425
427 def __init__(self, root, cwd, predfn, predrepr=None, badfn=None):
426 def __init__(self, root, cwd, predfn, predrepr=None, badfn=None):
428 super(predicatematcher, self).__init__(root, cwd, badfn)
427 super(predicatematcher, self).__init__(root, cwd, badfn)
429 self.matchfn = predfn
428 self.matchfn = predfn
430 self._predrepr = predrepr
429 self._predrepr = predrepr
431
430
432 @encoding.strmethod
431 @encoding.strmethod
433 def __repr__(self):
432 def __repr__(self):
434 s = (stringutil.buildrepr(self._predrepr)
433 s = (stringutil.buildrepr(self._predrepr)
435 or pycompat.byterepr(self.matchfn))
434 or pycompat.byterepr(self.matchfn))
436 return '<predicatenmatcher pred=%s>' % s
435 return '<predicatenmatcher pred=%s>' % s
437
436
438 class patternmatcher(basematcher):
437 class patternmatcher(basematcher):
439
438
440 def __init__(self, root, cwd, kindpats, listsubrepos=False, badfn=None):
439 def __init__(self, root, cwd, kindpats, badfn=None):
441 super(patternmatcher, self).__init__(root, cwd, badfn)
440 super(patternmatcher, self).__init__(root, cwd, badfn)
442
441
443 self._files = _explicitfiles(kindpats)
442 self._files = _explicitfiles(kindpats)
444 self._prefix = _prefix(kindpats)
443 self._prefix = _prefix(kindpats)
445 self._pats, self.matchfn = _buildmatch(kindpats, '$', listsubrepos,
444 self._pats, self.matchfn = _buildmatch(kindpats, '$', root)
446 root)
447
445
448 @propertycache
446 @propertycache
449 def _dirs(self):
447 def _dirs(self):
450 return set(util.dirs(self._fileset)) | {'.'}
448 return set(util.dirs(self._fileset)) | {'.'}
451
449
452 def visitdir(self, dir):
450 def visitdir(self, dir):
453 if self._prefix and dir in self._fileset:
451 if self._prefix and dir in self._fileset:
454 return 'all'
452 return 'all'
455 return ('.' in self._fileset or
453 return ('.' in self._fileset or
456 dir in self._fileset or
454 dir in self._fileset or
457 dir in self._dirs or
455 dir in self._dirs or
458 any(parentdir in self._fileset
456 any(parentdir in self._fileset
459 for parentdir in util.finddirs(dir)))
457 for parentdir in util.finddirs(dir)))
460
458
461 def visitchildrenset(self, dir):
459 def visitchildrenset(self, dir):
462 ret = self.visitdir(dir)
460 ret = self.visitdir(dir)
463 if ret is True:
461 if ret is True:
464 return 'this'
462 return 'this'
465 elif not ret:
463 elif not ret:
466 return set()
464 return set()
467 assert ret == 'all'
465 assert ret == 'all'
468 return 'all'
466 return 'all'
469
467
470 def prefix(self):
468 def prefix(self):
471 return self._prefix
469 return self._prefix
472
470
473 @encoding.strmethod
471 @encoding.strmethod
474 def __repr__(self):
472 def __repr__(self):
475 return ('<patternmatcher patterns=%r>' % pycompat.bytestr(self._pats))
473 return ('<patternmatcher patterns=%r>' % pycompat.bytestr(self._pats))
476
474
477 # This is basically a reimplementation of util.dirs that stores the children
475 # This is basically a reimplementation of util.dirs that stores the children
478 # instead of just a count of them, plus a small optional optimization to avoid
476 # instead of just a count of them, plus a small optional optimization to avoid
479 # some directories we don't need.
477 # some directories we don't need.
480 class _dirchildren(object):
478 class _dirchildren(object):
481 def __init__(self, paths, onlyinclude=None):
479 def __init__(self, paths, onlyinclude=None):
482 self._dirs = {}
480 self._dirs = {}
483 self._onlyinclude = onlyinclude or []
481 self._onlyinclude = onlyinclude or []
484 addpath = self.addpath
482 addpath = self.addpath
485 for f in paths:
483 for f in paths:
486 addpath(f)
484 addpath(f)
487
485
488 def addpath(self, path):
486 def addpath(self, path):
489 if path == '.':
487 if path == '.':
490 return
488 return
491 dirs = self._dirs
489 dirs = self._dirs
492 findsplitdirs = _dirchildren._findsplitdirs
490 findsplitdirs = _dirchildren._findsplitdirs
493 for d, b in findsplitdirs(path):
491 for d, b in findsplitdirs(path):
494 if d not in self._onlyinclude:
492 if d not in self._onlyinclude:
495 continue
493 continue
496 dirs.setdefault(d, set()).add(b)
494 dirs.setdefault(d, set()).add(b)
497
495
498 @staticmethod
496 @staticmethod
499 def _findsplitdirs(path):
497 def _findsplitdirs(path):
500 # yields (dirname, basename) tuples, walking back to the root. This is
498 # yields (dirname, basename) tuples, walking back to the root. This is
501 # very similar to util.finddirs, except:
499 # very similar to util.finddirs, except:
502 # - produces a (dirname, basename) tuple, not just 'dirname'
500 # - produces a (dirname, basename) tuple, not just 'dirname'
503 # - includes root dir
501 # - includes root dir
504 # Unlike manifest._splittopdir, this does not suffix `dirname` with a
502 # Unlike manifest._splittopdir, this does not suffix `dirname` with a
505 # slash, and produces '.' for the root instead of ''.
503 # slash, and produces '.' for the root instead of ''.
506 oldpos = len(path)
504 oldpos = len(path)
507 pos = path.rfind('/')
505 pos = path.rfind('/')
508 while pos != -1:
506 while pos != -1:
509 yield path[:pos], path[pos + 1:oldpos]
507 yield path[:pos], path[pos + 1:oldpos]
510 oldpos = pos
508 oldpos = pos
511 pos = path.rfind('/', 0, pos)
509 pos = path.rfind('/', 0, pos)
512 yield '.', path[:oldpos]
510 yield '.', path[:oldpos]
513
511
514 def get(self, path):
512 def get(self, path):
515 return self._dirs.get(path, set())
513 return self._dirs.get(path, set())
516
514
517 class includematcher(basematcher):
515 class includematcher(basematcher):
518
516
519 def __init__(self, root, cwd, kindpats, listsubrepos=False, badfn=None):
517 def __init__(self, root, cwd, kindpats, badfn=None):
520 super(includematcher, self).__init__(root, cwd, badfn)
518 super(includematcher, self).__init__(root, cwd, badfn)
521
519
522 self._pats, self.matchfn = _buildmatch(kindpats, '(?:/|$)',
520 self._pats, self.matchfn = _buildmatch(kindpats, '(?:/|$)', root)
523 listsubrepos, root)
524 self._prefix = _prefix(kindpats)
521 self._prefix = _prefix(kindpats)
525 roots, dirs, parents = _rootsdirsandparents(kindpats)
522 roots, dirs, parents = _rootsdirsandparents(kindpats)
526 # roots are directories which are recursively included.
523 # roots are directories which are recursively included.
527 self._roots = set(roots)
524 self._roots = set(roots)
528 # dirs are directories which are non-recursively included.
525 # dirs are directories which are non-recursively included.
529 self._dirs = set(dirs)
526 self._dirs = set(dirs)
530 # parents are directories which are non-recursively included because
527 # parents are directories which are non-recursively included because
531 # they are needed to get to items in _dirs or _roots.
528 # they are needed to get to items in _dirs or _roots.
532 self._parents = set(parents)
529 self._parents = set(parents)
533
530
534 def visitdir(self, dir):
531 def visitdir(self, dir):
535 if self._prefix and dir in self._roots:
532 if self._prefix and dir in self._roots:
536 return 'all'
533 return 'all'
537 return ('.' in self._roots or
534 return ('.' in self._roots or
538 dir in self._roots or
535 dir in self._roots or
539 dir in self._dirs or
536 dir in self._dirs or
540 dir in self._parents or
537 dir in self._parents or
541 any(parentdir in self._roots
538 any(parentdir in self._roots
542 for parentdir in util.finddirs(dir)))
539 for parentdir in util.finddirs(dir)))
543
540
544 @propertycache
541 @propertycache
545 def _allparentschildren(self):
542 def _allparentschildren(self):
546 # It may seem odd that we add dirs, roots, and parents, and then
543 # It may seem odd that we add dirs, roots, and parents, and then
547 # restrict to only parents. This is to catch the case of:
544 # restrict to only parents. This is to catch the case of:
548 # dirs = ['foo/bar']
545 # dirs = ['foo/bar']
549 # parents = ['foo']
546 # parents = ['foo']
550 # if we asked for the children of 'foo', but had only added
547 # if we asked for the children of 'foo', but had only added
551 # self._parents, we wouldn't be able to respond ['bar'].
548 # self._parents, we wouldn't be able to respond ['bar'].
552 return _dirchildren(
549 return _dirchildren(
553 itertools.chain(self._dirs, self._roots, self._parents),
550 itertools.chain(self._dirs, self._roots, self._parents),
554 onlyinclude=self._parents)
551 onlyinclude=self._parents)
555
552
556 def visitchildrenset(self, dir):
553 def visitchildrenset(self, dir):
557 if self._prefix and dir in self._roots:
554 if self._prefix and dir in self._roots:
558 return 'all'
555 return 'all'
559 # Note: this does *not* include the 'dir in self._parents' case from
556 # Note: this does *not* include the 'dir in self._parents' case from
560 # visitdir, that's handled below.
557 # visitdir, that's handled below.
561 if ('.' in self._roots or
558 if ('.' in self._roots or
562 dir in self._roots or
559 dir in self._roots or
563 dir in self._dirs or
560 dir in self._dirs or
564 any(parentdir in self._roots
561 any(parentdir in self._roots
565 for parentdir in util.finddirs(dir))):
562 for parentdir in util.finddirs(dir))):
566 return 'this'
563 return 'this'
567
564
568 if dir in self._parents:
565 if dir in self._parents:
569 return self._allparentschildren.get(dir) or set()
566 return self._allparentschildren.get(dir) or set()
570 return set()
567 return set()
571
568
572 @encoding.strmethod
569 @encoding.strmethod
573 def __repr__(self):
570 def __repr__(self):
574 return ('<includematcher includes=%r>' % pycompat.bytestr(self._pats))
571 return ('<includematcher includes=%r>' % pycompat.bytestr(self._pats))
575
572
576 class exactmatcher(basematcher):
573 class exactmatcher(basematcher):
577 '''Matches the input files exactly. They are interpreted as paths, not
574 '''Matches the input files exactly. They are interpreted as paths, not
578 patterns (so no kind-prefixes).
575 patterns (so no kind-prefixes).
579 '''
576 '''
580
577
581 def __init__(self, root, cwd, files, badfn=None):
578 def __init__(self, root, cwd, files, badfn=None):
582 super(exactmatcher, self).__init__(root, cwd, badfn)
579 super(exactmatcher, self).__init__(root, cwd, badfn)
583
580
584 if isinstance(files, list):
581 if isinstance(files, list):
585 self._files = files
582 self._files = files
586 else:
583 else:
587 self._files = list(files)
584 self._files = list(files)
588
585
589 matchfn = basematcher.exact
586 matchfn = basematcher.exact
590
587
591 @propertycache
588 @propertycache
592 def _dirs(self):
589 def _dirs(self):
593 return set(util.dirs(self._fileset)) | {'.'}
590 return set(util.dirs(self._fileset)) | {'.'}
594
591
595 def visitdir(self, dir):
592 def visitdir(self, dir):
596 return dir in self._dirs
593 return dir in self._dirs
597
594
598 def visitchildrenset(self, dir):
595 def visitchildrenset(self, dir):
599 if not self._fileset or dir not in self._dirs:
596 if not self._fileset or dir not in self._dirs:
600 return set()
597 return set()
601
598
602 candidates = self._fileset | self._dirs - {'.'}
599 candidates = self._fileset | self._dirs - {'.'}
603 if dir != '.':
600 if dir != '.':
604 d = dir + '/'
601 d = dir + '/'
605 candidates = set(c[len(d):] for c in candidates if
602 candidates = set(c[len(d):] for c in candidates if
606 c.startswith(d))
603 c.startswith(d))
607 # self._dirs includes all of the directories, recursively, so if
604 # self._dirs includes all of the directories, recursively, so if
608 # we're attempting to match foo/bar/baz.txt, it'll have '.', 'foo',
605 # we're attempting to match foo/bar/baz.txt, it'll have '.', 'foo',
609 # 'foo/bar' in it. Thus we can safely ignore a candidate that has a
606 # 'foo/bar' in it. Thus we can safely ignore a candidate that has a
610 # '/' in it, indicating a it's for a subdir-of-a-subdir; the
607 # '/' in it, indicating a it's for a subdir-of-a-subdir; the
611 # immediate subdir will be in there without a slash.
608 # immediate subdir will be in there without a slash.
612 ret = {c for c in candidates if '/' not in c}
609 ret = {c for c in candidates if '/' not in c}
613 # We really do not expect ret to be empty, since that would imply that
610 # We really do not expect ret to be empty, since that would imply that
614 # there's something in _dirs that didn't have a file in _fileset.
611 # there's something in _dirs that didn't have a file in _fileset.
615 assert ret
612 assert ret
616 return ret
613 return ret
617
614
618 def isexact(self):
615 def isexact(self):
619 return True
616 return True
620
617
621 @encoding.strmethod
618 @encoding.strmethod
622 def __repr__(self):
619 def __repr__(self):
623 return ('<exactmatcher files=%r>' % self._files)
620 return ('<exactmatcher files=%r>' % self._files)
624
621
625 class differencematcher(basematcher):
622 class differencematcher(basematcher):
626 '''Composes two matchers by matching if the first matches and the second
623 '''Composes two matchers by matching if the first matches and the second
627 does not.
624 does not.
628
625
629 The second matcher's non-matching-attributes (root, cwd, bad, explicitdir,
626 The second matcher's non-matching-attributes (root, cwd, bad, explicitdir,
630 traversedir) are ignored.
627 traversedir) are ignored.
631 '''
628 '''
632 def __init__(self, m1, m2):
629 def __init__(self, m1, m2):
633 super(differencematcher, self).__init__(m1._root, m1._cwd)
630 super(differencematcher, self).__init__(m1._root, m1._cwd)
634 self._m1 = m1
631 self._m1 = m1
635 self._m2 = m2
632 self._m2 = m2
636 self.bad = m1.bad
633 self.bad = m1.bad
637 self.explicitdir = m1.explicitdir
634 self.explicitdir = m1.explicitdir
638 self.traversedir = m1.traversedir
635 self.traversedir = m1.traversedir
639
636
640 def matchfn(self, f):
637 def matchfn(self, f):
641 return self._m1(f) and not self._m2(f)
638 return self._m1(f) and not self._m2(f)
642
639
643 @propertycache
640 @propertycache
644 def _files(self):
641 def _files(self):
645 if self.isexact():
642 if self.isexact():
646 return [f for f in self._m1.files() if self(f)]
643 return [f for f in self._m1.files() if self(f)]
647 # If m1 is not an exact matcher, we can't easily figure out the set of
644 # If m1 is not an exact matcher, we can't easily figure out the set of
648 # files, because its files() are not always files. For example, if
645 # files, because its files() are not always files. For example, if
649 # m1 is "path:dir" and m2 is "rootfileins:.", we don't
646 # m1 is "path:dir" and m2 is "rootfileins:.", we don't
650 # want to remove "dir" from the set even though it would match m2,
647 # want to remove "dir" from the set even though it would match m2,
651 # because the "dir" in m1 may not be a file.
648 # because the "dir" in m1 may not be a file.
652 return self._m1.files()
649 return self._m1.files()
653
650
654 def visitdir(self, dir):
651 def visitdir(self, dir):
655 if self._m2.visitdir(dir) == 'all':
652 if self._m2.visitdir(dir) == 'all':
656 return False
653 return False
657 elif not self._m2.visitdir(dir):
654 elif not self._m2.visitdir(dir):
658 # m2 does not match dir, we can return 'all' here if possible
655 # m2 does not match dir, we can return 'all' here if possible
659 return self._m1.visitdir(dir)
656 return self._m1.visitdir(dir)
660 return bool(self._m1.visitdir(dir))
657 return bool(self._m1.visitdir(dir))
661
658
662 def visitchildrenset(self, dir):
659 def visitchildrenset(self, dir):
663 m2_set = self._m2.visitchildrenset(dir)
660 m2_set = self._m2.visitchildrenset(dir)
664 if m2_set == 'all':
661 if m2_set == 'all':
665 return set()
662 return set()
666 m1_set = self._m1.visitchildrenset(dir)
663 m1_set = self._m1.visitchildrenset(dir)
667 # Possible values for m1: 'all', 'this', set(...), set()
664 # Possible values for m1: 'all', 'this', set(...), set()
668 # Possible values for m2: 'this', set(...), set()
665 # Possible values for m2: 'this', set(...), set()
669 # If m2 has nothing under here that we care about, return m1, even if
666 # If m2 has nothing under here that we care about, return m1, even if
670 # it's 'all'. This is a change in behavior from visitdir, which would
667 # it's 'all'. This is a change in behavior from visitdir, which would
671 # return True, not 'all', for some reason.
668 # return True, not 'all', for some reason.
672 if not m2_set:
669 if not m2_set:
673 return m1_set
670 return m1_set
674 if m1_set in ['all', 'this']:
671 if m1_set in ['all', 'this']:
675 # Never return 'all' here if m2_set is any kind of non-empty (either
672 # Never return 'all' here if m2_set is any kind of non-empty (either
676 # 'this' or set(foo)), since m2 might return set() for a
673 # 'this' or set(foo)), since m2 might return set() for a
677 # subdirectory.
674 # subdirectory.
678 return 'this'
675 return 'this'
679 # Possible values for m1: set(...), set()
676 # Possible values for m1: set(...), set()
680 # Possible values for m2: 'this', set(...)
677 # Possible values for m2: 'this', set(...)
681 # We ignore m2's set results. They're possibly incorrect:
678 # We ignore m2's set results. They're possibly incorrect:
682 # m1 = path:dir/subdir, m2=rootfilesin:dir, visitchildrenset('.'):
679 # m1 = path:dir/subdir, m2=rootfilesin:dir, visitchildrenset('.'):
683 # m1 returns {'dir'}, m2 returns {'dir'}, if we subtracted we'd
680 # m1 returns {'dir'}, m2 returns {'dir'}, if we subtracted we'd
684 # return set(), which is *not* correct, we still need to visit 'dir'!
681 # return set(), which is *not* correct, we still need to visit 'dir'!
685 return m1_set
682 return m1_set
686
683
687 def isexact(self):
684 def isexact(self):
688 return self._m1.isexact()
685 return self._m1.isexact()
689
686
690 @encoding.strmethod
687 @encoding.strmethod
691 def __repr__(self):
688 def __repr__(self):
692 return ('<differencematcher m1=%r, m2=%r>' % (self._m1, self._m2))
689 return ('<differencematcher m1=%r, m2=%r>' % (self._m1, self._m2))
693
690
694 def intersectmatchers(m1, m2):
691 def intersectmatchers(m1, m2):
695 '''Composes two matchers by matching if both of them match.
692 '''Composes two matchers by matching if both of them match.
696
693
697 The second matcher's non-matching-attributes (root, cwd, bad, explicitdir,
694 The second matcher's non-matching-attributes (root, cwd, bad, explicitdir,
698 traversedir) are ignored.
695 traversedir) are ignored.
699 '''
696 '''
700 if m1 is None or m2 is None:
697 if m1 is None or m2 is None:
701 return m1 or m2
698 return m1 or m2
702 if m1.always():
699 if m1.always():
703 m = copy.copy(m2)
700 m = copy.copy(m2)
704 # TODO: Consider encapsulating these things in a class so there's only
701 # TODO: Consider encapsulating these things in a class so there's only
705 # one thing to copy from m1.
702 # one thing to copy from m1.
706 m.bad = m1.bad
703 m.bad = m1.bad
707 m.explicitdir = m1.explicitdir
704 m.explicitdir = m1.explicitdir
708 m.traversedir = m1.traversedir
705 m.traversedir = m1.traversedir
709 return m
706 return m
710 if m2.always():
707 if m2.always():
711 m = copy.copy(m1)
708 m = copy.copy(m1)
712 return m
709 return m
713 return intersectionmatcher(m1, m2)
710 return intersectionmatcher(m1, m2)
714
711
715 class intersectionmatcher(basematcher):
712 class intersectionmatcher(basematcher):
716 def __init__(self, m1, m2):
713 def __init__(self, m1, m2):
717 super(intersectionmatcher, self).__init__(m1._root, m1._cwd)
714 super(intersectionmatcher, self).__init__(m1._root, m1._cwd)
718 self._m1 = m1
715 self._m1 = m1
719 self._m2 = m2
716 self._m2 = m2
720 self.bad = m1.bad
717 self.bad = m1.bad
721 self.explicitdir = m1.explicitdir
718 self.explicitdir = m1.explicitdir
722 self.traversedir = m1.traversedir
719 self.traversedir = m1.traversedir
723
720
724 @propertycache
721 @propertycache
725 def _files(self):
722 def _files(self):
726 if self.isexact():
723 if self.isexact():
727 m1, m2 = self._m1, self._m2
724 m1, m2 = self._m1, self._m2
728 if not m1.isexact():
725 if not m1.isexact():
729 m1, m2 = m2, m1
726 m1, m2 = m2, m1
730 return [f for f in m1.files() if m2(f)]
727 return [f for f in m1.files() if m2(f)]
731 # It neither m1 nor m2 is an exact matcher, we can't easily intersect
728 # It neither m1 nor m2 is an exact matcher, we can't easily intersect
732 # the set of files, because their files() are not always files. For
729 # the set of files, because their files() are not always files. For
733 # example, if intersecting a matcher "-I glob:foo.txt" with matcher of
730 # example, if intersecting a matcher "-I glob:foo.txt" with matcher of
734 # "path:dir2", we don't want to remove "dir2" from the set.
731 # "path:dir2", we don't want to remove "dir2" from the set.
735 return self._m1.files() + self._m2.files()
732 return self._m1.files() + self._m2.files()
736
733
737 def matchfn(self, f):
734 def matchfn(self, f):
738 return self._m1(f) and self._m2(f)
735 return self._m1(f) and self._m2(f)
739
736
740 def visitdir(self, dir):
737 def visitdir(self, dir):
741 visit1 = self._m1.visitdir(dir)
738 visit1 = self._m1.visitdir(dir)
742 if visit1 == 'all':
739 if visit1 == 'all':
743 return self._m2.visitdir(dir)
740 return self._m2.visitdir(dir)
744 # bool() because visit1=True + visit2='all' should not be 'all'
741 # bool() because visit1=True + visit2='all' should not be 'all'
745 return bool(visit1 and self._m2.visitdir(dir))
742 return bool(visit1 and self._m2.visitdir(dir))
746
743
747 def visitchildrenset(self, dir):
744 def visitchildrenset(self, dir):
748 m1_set = self._m1.visitchildrenset(dir)
745 m1_set = self._m1.visitchildrenset(dir)
749 if not m1_set:
746 if not m1_set:
750 return set()
747 return set()
751 m2_set = self._m2.visitchildrenset(dir)
748 m2_set = self._m2.visitchildrenset(dir)
752 if not m2_set:
749 if not m2_set:
753 return set()
750 return set()
754
751
755 if m1_set == 'all':
752 if m1_set == 'all':
756 return m2_set
753 return m2_set
757 elif m2_set == 'all':
754 elif m2_set == 'all':
758 return m1_set
755 return m1_set
759
756
760 if m1_set == 'this' or m2_set == 'this':
757 if m1_set == 'this' or m2_set == 'this':
761 return 'this'
758 return 'this'
762
759
763 assert isinstance(m1_set, set) and isinstance(m2_set, set)
760 assert isinstance(m1_set, set) and isinstance(m2_set, set)
764 return m1_set.intersection(m2_set)
761 return m1_set.intersection(m2_set)
765
762
766 def always(self):
763 def always(self):
767 return self._m1.always() and self._m2.always()
764 return self._m1.always() and self._m2.always()
768
765
769 def isexact(self):
766 def isexact(self):
770 return self._m1.isexact() or self._m2.isexact()
767 return self._m1.isexact() or self._m2.isexact()
771
768
772 @encoding.strmethod
769 @encoding.strmethod
773 def __repr__(self):
770 def __repr__(self):
774 return ('<intersectionmatcher m1=%r, m2=%r>' % (self._m1, self._m2))
771 return ('<intersectionmatcher m1=%r, m2=%r>' % (self._m1, self._m2))
775
772
776 class subdirmatcher(basematcher):
773 class subdirmatcher(basematcher):
777 """Adapt a matcher to work on a subdirectory only.
774 """Adapt a matcher to work on a subdirectory only.
778
775
779 The paths are remapped to remove/insert the path as needed:
776 The paths are remapped to remove/insert the path as needed:
780
777
781 >>> from . import pycompat
778 >>> from . import pycompat
782 >>> m1 = match(b'root', b'', [b'a.txt', b'sub/b.txt'])
779 >>> m1 = match(b'root', b'', [b'a.txt', b'sub/b.txt'])
783 >>> m2 = subdirmatcher(b'sub', m1)
780 >>> m2 = subdirmatcher(b'sub', m1)
784 >>> bool(m2(b'a.txt'))
781 >>> bool(m2(b'a.txt'))
785 False
782 False
786 >>> bool(m2(b'b.txt'))
783 >>> bool(m2(b'b.txt'))
787 True
784 True
788 >>> bool(m2.matchfn(b'a.txt'))
785 >>> bool(m2.matchfn(b'a.txt'))
789 False
786 False
790 >>> bool(m2.matchfn(b'b.txt'))
787 >>> bool(m2.matchfn(b'b.txt'))
791 True
788 True
792 >>> m2.files()
789 >>> m2.files()
793 ['b.txt']
790 ['b.txt']
794 >>> m2.exact(b'b.txt')
791 >>> m2.exact(b'b.txt')
795 True
792 True
796 >>> def bad(f, msg):
793 >>> def bad(f, msg):
797 ... print(pycompat.sysstr(b"%s: %s" % (f, msg)))
794 ... print(pycompat.sysstr(b"%s: %s" % (f, msg)))
798 >>> m1.bad = bad
795 >>> m1.bad = bad
799 >>> m2.bad(b'x.txt', b'No such file')
796 >>> m2.bad(b'x.txt', b'No such file')
800 sub/x.txt: No such file
797 sub/x.txt: No such file
801 """
798 """
802
799
803 def __init__(self, path, matcher):
800 def __init__(self, path, matcher):
804 super(subdirmatcher, self).__init__(matcher._root, matcher._cwd)
801 super(subdirmatcher, self).__init__(matcher._root, matcher._cwd)
805 self._path = path
802 self._path = path
806 self._matcher = matcher
803 self._matcher = matcher
807 self._always = matcher.always()
804 self._always = matcher.always()
808
805
809 self._files = [f[len(path) + 1:] for f in matcher._files
806 self._files = [f[len(path) + 1:] for f in matcher._files
810 if f.startswith(path + "/")]
807 if f.startswith(path + "/")]
811
808
812 # If the parent repo had a path to this subrepo and the matcher is
809 # If the parent repo had a path to this subrepo and the matcher is
813 # a prefix matcher, this submatcher always matches.
810 # a prefix matcher, this submatcher always matches.
814 if matcher.prefix():
811 if matcher.prefix():
815 self._always = any(f == path for f in matcher._files)
812 self._always = any(f == path for f in matcher._files)
816
813
817 def bad(self, f, msg):
814 def bad(self, f, msg):
818 self._matcher.bad(self._path + "/" + f, msg)
815 self._matcher.bad(self._path + "/" + f, msg)
819
816
820 def matchfn(self, f):
817 def matchfn(self, f):
821 # Some information is lost in the superclass's constructor, so we
818 # Some information is lost in the superclass's constructor, so we
822 # can not accurately create the matching function for the subdirectory
819 # can not accurately create the matching function for the subdirectory
823 # from the inputs. Instead, we override matchfn() and visitdir() to
820 # from the inputs. Instead, we override matchfn() and visitdir() to
824 # call the original matcher with the subdirectory path prepended.
821 # call the original matcher with the subdirectory path prepended.
825 return self._matcher.matchfn(self._path + "/" + f)
822 return self._matcher.matchfn(self._path + "/" + f)
826
823
827 def visitdir(self, dir):
824 def visitdir(self, dir):
828 if dir == '.':
825 if dir == '.':
829 dir = self._path
826 dir = self._path
830 else:
827 else:
831 dir = self._path + "/" + dir
828 dir = self._path + "/" + dir
832 return self._matcher.visitdir(dir)
829 return self._matcher.visitdir(dir)
833
830
834 def visitchildrenset(self, dir):
831 def visitchildrenset(self, dir):
835 if dir == '.':
832 if dir == '.':
836 dir = self._path
833 dir = self._path
837 else:
834 else:
838 dir = self._path + "/" + dir
835 dir = self._path + "/" + dir
839 return self._matcher.visitchildrenset(dir)
836 return self._matcher.visitchildrenset(dir)
840
837
841 def always(self):
838 def always(self):
842 return self._always
839 return self._always
843
840
844 def prefix(self):
841 def prefix(self):
845 return self._matcher.prefix() and not self._always
842 return self._matcher.prefix() and not self._always
846
843
847 @encoding.strmethod
844 @encoding.strmethod
848 def __repr__(self):
845 def __repr__(self):
849 return ('<subdirmatcher path=%r, matcher=%r>' %
846 return ('<subdirmatcher path=%r, matcher=%r>' %
850 (self._path, self._matcher))
847 (self._path, self._matcher))
851
848
852 class prefixdirmatcher(basematcher):
849 class prefixdirmatcher(basematcher):
853 """Adapt a matcher to work on a parent directory.
850 """Adapt a matcher to work on a parent directory.
854
851
855 The matcher's non-matching-attributes (root, cwd, bad, explicitdir,
852 The matcher's non-matching-attributes (root, cwd, bad, explicitdir,
856 traversedir) are ignored.
853 traversedir) are ignored.
857
854
858 The prefix path should usually be the relative path from the root of
855 The prefix path should usually be the relative path from the root of
859 this matcher to the root of the wrapped matcher.
856 this matcher to the root of the wrapped matcher.
860
857
861 >>> m1 = match(util.localpath(b'root/d/e'), b'f', [b'../a.txt', b'b.txt'])
858 >>> m1 = match(util.localpath(b'root/d/e'), b'f', [b'../a.txt', b'b.txt'])
862 >>> m2 = prefixdirmatcher(b'root', b'd/e/f', b'd/e', m1)
859 >>> m2 = prefixdirmatcher(b'root', b'd/e/f', b'd/e', m1)
863 >>> bool(m2(b'a.txt'),)
860 >>> bool(m2(b'a.txt'),)
864 False
861 False
865 >>> bool(m2(b'd/e/a.txt'))
862 >>> bool(m2(b'd/e/a.txt'))
866 True
863 True
867 >>> bool(m2(b'd/e/b.txt'))
864 >>> bool(m2(b'd/e/b.txt'))
868 False
865 False
869 >>> m2.files()
866 >>> m2.files()
870 ['d/e/a.txt', 'd/e/f/b.txt']
867 ['d/e/a.txt', 'd/e/f/b.txt']
871 >>> m2.exact(b'd/e/a.txt')
868 >>> m2.exact(b'd/e/a.txt')
872 True
869 True
873 >>> m2.visitdir(b'd')
870 >>> m2.visitdir(b'd')
874 True
871 True
875 >>> m2.visitdir(b'd/e')
872 >>> m2.visitdir(b'd/e')
876 True
873 True
877 >>> m2.visitdir(b'd/e/f')
874 >>> m2.visitdir(b'd/e/f')
878 True
875 True
879 >>> m2.visitdir(b'd/e/g')
876 >>> m2.visitdir(b'd/e/g')
880 False
877 False
881 >>> m2.visitdir(b'd/ef')
878 >>> m2.visitdir(b'd/ef')
882 False
879 False
883 """
880 """
884
881
885 def __init__(self, root, cwd, path, matcher, badfn=None):
882 def __init__(self, root, cwd, path, matcher, badfn=None):
886 super(prefixdirmatcher, self).__init__(root, cwd, badfn)
883 super(prefixdirmatcher, self).__init__(root, cwd, badfn)
887 if not path:
884 if not path:
888 raise error.ProgrammingError('prefix path must not be empty')
885 raise error.ProgrammingError('prefix path must not be empty')
889 self._path = path
886 self._path = path
890 self._pathprefix = path + '/'
887 self._pathprefix = path + '/'
891 self._matcher = matcher
888 self._matcher = matcher
892
889
893 @propertycache
890 @propertycache
894 def _files(self):
891 def _files(self):
895 return [self._pathprefix + f for f in self._matcher._files]
892 return [self._pathprefix + f for f in self._matcher._files]
896
893
897 def matchfn(self, f):
894 def matchfn(self, f):
898 if not f.startswith(self._pathprefix):
895 if not f.startswith(self._pathprefix):
899 return False
896 return False
900 return self._matcher.matchfn(f[len(self._pathprefix):])
897 return self._matcher.matchfn(f[len(self._pathprefix):])
901
898
902 @propertycache
899 @propertycache
903 def _pathdirs(self):
900 def _pathdirs(self):
904 return set(util.finddirs(self._path)) | {'.'}
901 return set(util.finddirs(self._path)) | {'.'}
905
902
906 def visitdir(self, dir):
903 def visitdir(self, dir):
907 if dir == self._path:
904 if dir == self._path:
908 return self._matcher.visitdir('.')
905 return self._matcher.visitdir('.')
909 if dir.startswith(self._pathprefix):
906 if dir.startswith(self._pathprefix):
910 return self._matcher.visitdir(dir[len(self._pathprefix):])
907 return self._matcher.visitdir(dir[len(self._pathprefix):])
911 return dir in self._pathdirs
908 return dir in self._pathdirs
912
909
913 def visitchildrenset(self, dir):
910 def visitchildrenset(self, dir):
914 if dir == self._path:
911 if dir == self._path:
915 return self._matcher.visitchildrenset('.')
912 return self._matcher.visitchildrenset('.')
916 if dir.startswith(self._pathprefix):
913 if dir.startswith(self._pathprefix):
917 return self._matcher.visitchildrenset(dir[len(self._pathprefix):])
914 return self._matcher.visitchildrenset(dir[len(self._pathprefix):])
918 if dir in self._pathdirs:
915 if dir in self._pathdirs:
919 return 'this'
916 return 'this'
920 return set()
917 return set()
921
918
922 def isexact(self):
919 def isexact(self):
923 return self._matcher.isexact()
920 return self._matcher.isexact()
924
921
925 def prefix(self):
922 def prefix(self):
926 return self._matcher.prefix()
923 return self._matcher.prefix()
927
924
928 @encoding.strmethod
925 @encoding.strmethod
929 def __repr__(self):
926 def __repr__(self):
930 return ('<prefixdirmatcher path=%r, matcher=%r>'
927 return ('<prefixdirmatcher path=%r, matcher=%r>'
931 % (pycompat.bytestr(self._path), self._matcher))
928 % (pycompat.bytestr(self._path), self._matcher))
932
929
933 class unionmatcher(basematcher):
930 class unionmatcher(basematcher):
934 """A matcher that is the union of several matchers.
931 """A matcher that is the union of several matchers.
935
932
936 The non-matching-attributes (root, cwd, bad, explicitdir, traversedir) are
933 The non-matching-attributes (root, cwd, bad, explicitdir, traversedir) are
937 taken from the first matcher.
934 taken from the first matcher.
938 """
935 """
939
936
940 def __init__(self, matchers):
937 def __init__(self, matchers):
941 m1 = matchers[0]
938 m1 = matchers[0]
942 super(unionmatcher, self).__init__(m1._root, m1._cwd)
939 super(unionmatcher, self).__init__(m1._root, m1._cwd)
943 self.explicitdir = m1.explicitdir
940 self.explicitdir = m1.explicitdir
944 self.traversedir = m1.traversedir
941 self.traversedir = m1.traversedir
945 self._matchers = matchers
942 self._matchers = matchers
946
943
947 def matchfn(self, f):
944 def matchfn(self, f):
948 for match in self._matchers:
945 for match in self._matchers:
949 if match(f):
946 if match(f):
950 return True
947 return True
951 return False
948 return False
952
949
953 def visitdir(self, dir):
950 def visitdir(self, dir):
954 r = False
951 r = False
955 for m in self._matchers:
952 for m in self._matchers:
956 v = m.visitdir(dir)
953 v = m.visitdir(dir)
957 if v == 'all':
954 if v == 'all':
958 return v
955 return v
959 r |= v
956 r |= v
960 return r
957 return r
961
958
962 def visitchildrenset(self, dir):
959 def visitchildrenset(self, dir):
963 r = set()
960 r = set()
964 this = False
961 this = False
965 for m in self._matchers:
962 for m in self._matchers:
966 v = m.visitchildrenset(dir)
963 v = m.visitchildrenset(dir)
967 if not v:
964 if not v:
968 continue
965 continue
969 if v == 'all':
966 if v == 'all':
970 return v
967 return v
971 if this or v == 'this':
968 if this or v == 'this':
972 this = True
969 this = True
973 # don't break, we might have an 'all' in here.
970 # don't break, we might have an 'all' in here.
974 continue
971 continue
975 assert isinstance(v, set)
972 assert isinstance(v, set)
976 r = r.union(v)
973 r = r.union(v)
977 if this:
974 if this:
978 return 'this'
975 return 'this'
979 return r
976 return r
980
977
981 @encoding.strmethod
978 @encoding.strmethod
982 def __repr__(self):
979 def __repr__(self):
983 return ('<unionmatcher matchers=%r>' % self._matchers)
980 return ('<unionmatcher matchers=%r>' % self._matchers)
984
981
985 def patkind(pattern, default=None):
982 def patkind(pattern, default=None):
986 '''If pattern is 'kind:pat' with a known kind, return kind.'''
983 '''If pattern is 'kind:pat' with a known kind, return kind.'''
987 return _patsplit(pattern, default)[0]
984 return _patsplit(pattern, default)[0]
988
985
989 def _patsplit(pattern, default):
986 def _patsplit(pattern, default):
990 """Split a string into the optional pattern kind prefix and the actual
987 """Split a string into the optional pattern kind prefix and the actual
991 pattern."""
988 pattern."""
992 if ':' in pattern:
989 if ':' in pattern:
993 kind, pat = pattern.split(':', 1)
990 kind, pat = pattern.split(':', 1)
994 if kind in allpatternkinds:
991 if kind in allpatternkinds:
995 return kind, pat
992 return kind, pat
996 return default, pattern
993 return default, pattern
997
994
998 def _globre(pat):
995 def _globre(pat):
999 r'''Convert an extended glob string to a regexp string.
996 r'''Convert an extended glob string to a regexp string.
1000
997
1001 >>> from . import pycompat
998 >>> from . import pycompat
1002 >>> def bprint(s):
999 >>> def bprint(s):
1003 ... print(pycompat.sysstr(s))
1000 ... print(pycompat.sysstr(s))
1004 >>> bprint(_globre(br'?'))
1001 >>> bprint(_globre(br'?'))
1005 .
1002 .
1006 >>> bprint(_globre(br'*'))
1003 >>> bprint(_globre(br'*'))
1007 [^/]*
1004 [^/]*
1008 >>> bprint(_globre(br'**'))
1005 >>> bprint(_globre(br'**'))
1009 .*
1006 .*
1010 >>> bprint(_globre(br'**/a'))
1007 >>> bprint(_globre(br'**/a'))
1011 (?:.*/)?a
1008 (?:.*/)?a
1012 >>> bprint(_globre(br'a/**/b'))
1009 >>> bprint(_globre(br'a/**/b'))
1013 a/(?:.*/)?b
1010 a/(?:.*/)?b
1014 >>> bprint(_globre(br'[a*?!^][^b][!c]'))
1011 >>> bprint(_globre(br'[a*?!^][^b][!c]'))
1015 [a*?!^][\^b][^c]
1012 [a*?!^][\^b][^c]
1016 >>> bprint(_globre(br'{a,b}'))
1013 >>> bprint(_globre(br'{a,b}'))
1017 (?:a|b)
1014 (?:a|b)
1018 >>> bprint(_globre(br'.\*\?'))
1015 >>> bprint(_globre(br'.\*\?'))
1019 \.\*\?
1016 \.\*\?
1020 '''
1017 '''
1021 i, n = 0, len(pat)
1018 i, n = 0, len(pat)
1022 res = ''
1019 res = ''
1023 group = 0
1020 group = 0
1024 escape = util.stringutil.regexbytesescapemap.get
1021 escape = util.stringutil.regexbytesescapemap.get
1025 def peek():
1022 def peek():
1026 return i < n and pat[i:i + 1]
1023 return i < n and pat[i:i + 1]
1027 while i < n:
1024 while i < n:
1028 c = pat[i:i + 1]
1025 c = pat[i:i + 1]
1029 i += 1
1026 i += 1
1030 if c not in '*?[{},\\':
1027 if c not in '*?[{},\\':
1031 res += escape(c, c)
1028 res += escape(c, c)
1032 elif c == '*':
1029 elif c == '*':
1033 if peek() == '*':
1030 if peek() == '*':
1034 i += 1
1031 i += 1
1035 if peek() == '/':
1032 if peek() == '/':
1036 i += 1
1033 i += 1
1037 res += '(?:.*/)?'
1034 res += '(?:.*/)?'
1038 else:
1035 else:
1039 res += '.*'
1036 res += '.*'
1040 else:
1037 else:
1041 res += '[^/]*'
1038 res += '[^/]*'
1042 elif c == '?':
1039 elif c == '?':
1043 res += '.'
1040 res += '.'
1044 elif c == '[':
1041 elif c == '[':
1045 j = i
1042 j = i
1046 if j < n and pat[j:j + 1] in '!]':
1043 if j < n and pat[j:j + 1] in '!]':
1047 j += 1
1044 j += 1
1048 while j < n and pat[j:j + 1] != ']':
1045 while j < n and pat[j:j + 1] != ']':
1049 j += 1
1046 j += 1
1050 if j >= n:
1047 if j >= n:
1051 res += '\\['
1048 res += '\\['
1052 else:
1049 else:
1053 stuff = pat[i:j].replace('\\','\\\\')
1050 stuff = pat[i:j].replace('\\','\\\\')
1054 i = j + 1
1051 i = j + 1
1055 if stuff[0:1] == '!':
1052 if stuff[0:1] == '!':
1056 stuff = '^' + stuff[1:]
1053 stuff = '^' + stuff[1:]
1057 elif stuff[0:1] == '^':
1054 elif stuff[0:1] == '^':
1058 stuff = '\\' + stuff
1055 stuff = '\\' + stuff
1059 res = '%s[%s]' % (res, stuff)
1056 res = '%s[%s]' % (res, stuff)
1060 elif c == '{':
1057 elif c == '{':
1061 group += 1
1058 group += 1
1062 res += '(?:'
1059 res += '(?:'
1063 elif c == '}' and group:
1060 elif c == '}' and group:
1064 res += ')'
1061 res += ')'
1065 group -= 1
1062 group -= 1
1066 elif c == ',' and group:
1063 elif c == ',' and group:
1067 res += '|'
1064 res += '|'
1068 elif c == '\\':
1065 elif c == '\\':
1069 p = peek()
1066 p = peek()
1070 if p:
1067 if p:
1071 i += 1
1068 i += 1
1072 res += escape(p, p)
1069 res += escape(p, p)
1073 else:
1070 else:
1074 res += escape(c, c)
1071 res += escape(c, c)
1075 else:
1072 else:
1076 res += escape(c, c)
1073 res += escape(c, c)
1077 return res
1074 return res
1078
1075
1079 def _regex(kind, pat, globsuffix):
1076 def _regex(kind, pat, globsuffix):
1080 '''Convert a (normalized) pattern of any kind into a regular expression.
1077 '''Convert a (normalized) pattern of any kind into a regular expression.
1081 globsuffix is appended to the regexp of globs.'''
1078 globsuffix is appended to the regexp of globs.'''
1082 if not pat:
1079 if not pat:
1083 return ''
1080 return ''
1084 if kind == 're':
1081 if kind == 're':
1085 return pat
1082 return pat
1086 if kind in ('path', 'relpath'):
1083 if kind in ('path', 'relpath'):
1087 if pat == '.':
1084 if pat == '.':
1088 return ''
1085 return ''
1089 return util.stringutil.reescape(pat) + '(?:/|$)'
1086 return util.stringutil.reescape(pat) + '(?:/|$)'
1090 if kind == 'rootfilesin':
1087 if kind == 'rootfilesin':
1091 if pat == '.':
1088 if pat == '.':
1092 escaped = ''
1089 escaped = ''
1093 else:
1090 else:
1094 # Pattern is a directory name.
1091 # Pattern is a directory name.
1095 escaped = util.stringutil.reescape(pat) + '/'
1092 escaped = util.stringutil.reescape(pat) + '/'
1096 # Anything after the pattern must be a non-directory.
1093 # Anything after the pattern must be a non-directory.
1097 return escaped + '[^/]+$'
1094 return escaped + '[^/]+$'
1098 if kind == 'relglob':
1095 if kind == 'relglob':
1099 return '(?:|.*/)' + _globre(pat) + globsuffix
1096 return '(?:|.*/)' + _globre(pat) + globsuffix
1100 if kind == 'relre':
1097 if kind == 'relre':
1101 if pat.startswith('^'):
1098 if pat.startswith('^'):
1102 return pat
1099 return pat
1103 return '.*' + pat
1100 return '.*' + pat
1104 if kind in ('glob', 'rootglob'):
1101 if kind in ('glob', 'rootglob'):
1105 return _globre(pat) + globsuffix
1102 return _globre(pat) + globsuffix
1106 raise error.ProgrammingError('not a regex pattern: %s:%s' % (kind, pat))
1103 raise error.ProgrammingError('not a regex pattern: %s:%s' % (kind, pat))
1107
1104
1108 def _buildmatch(kindpats, globsuffix, listsubrepos, root):
1105 def _buildmatch(kindpats, globsuffix, root):
1109 '''Return regexp string and a matcher function for kindpats.
1106 '''Return regexp string and a matcher function for kindpats.
1110 globsuffix is appended to the regexp of globs.'''
1107 globsuffix is appended to the regexp of globs.'''
1111 matchfuncs = []
1108 matchfuncs = []
1112
1109
1113 subincludes, kindpats = _expandsubinclude(kindpats, root)
1110 subincludes, kindpats = _expandsubinclude(kindpats, root)
1114 if subincludes:
1111 if subincludes:
1115 submatchers = {}
1112 submatchers = {}
1116 def matchsubinclude(f):
1113 def matchsubinclude(f):
1117 for prefix, matcherargs in subincludes:
1114 for prefix, matcherargs in subincludes:
1118 if f.startswith(prefix):
1115 if f.startswith(prefix):
1119 mf = submatchers.get(prefix)
1116 mf = submatchers.get(prefix)
1120 if mf is None:
1117 if mf is None:
1121 mf = match(*matcherargs)
1118 mf = match(*matcherargs)
1122 submatchers[prefix] = mf
1119 submatchers[prefix] = mf
1123
1120
1124 if mf(f[len(prefix):]):
1121 if mf(f[len(prefix):]):
1125 return True
1122 return True
1126 return False
1123 return False
1127 matchfuncs.append(matchsubinclude)
1124 matchfuncs.append(matchsubinclude)
1128
1125
1129 regex = ''
1126 regex = ''
1130 if kindpats:
1127 if kindpats:
1131 if all(k == 'rootfilesin' for k, p, s in kindpats):
1128 if all(k == 'rootfilesin' for k, p, s in kindpats):
1132 dirs = {p for k, p, s in kindpats}
1129 dirs = {p for k, p, s in kindpats}
1133 def mf(f):
1130 def mf(f):
1134 i = f.rfind('/')
1131 i = f.rfind('/')
1135 if i >= 0:
1132 if i >= 0:
1136 dir = f[:i]
1133 dir = f[:i]
1137 else:
1134 else:
1138 dir = '.'
1135 dir = '.'
1139 return dir in dirs
1136 return dir in dirs
1140 regex = b'rootfilesin: %s' % stringutil.pprint(list(sorted(dirs)))
1137 regex = b'rootfilesin: %s' % stringutil.pprint(list(sorted(dirs)))
1141 matchfuncs.append(mf)
1138 matchfuncs.append(mf)
1142 else:
1139 else:
1143 regex, mf = _buildregexmatch(kindpats, globsuffix)
1140 regex, mf = _buildregexmatch(kindpats, globsuffix)
1144 matchfuncs.append(mf)
1141 matchfuncs.append(mf)
1145
1142
1146 if len(matchfuncs) == 1:
1143 if len(matchfuncs) == 1:
1147 return regex, matchfuncs[0]
1144 return regex, matchfuncs[0]
1148 else:
1145 else:
1149 return regex, lambda f: any(mf(f) for mf in matchfuncs)
1146 return regex, lambda f: any(mf(f) for mf in matchfuncs)
1150
1147
1151 MAX_RE_SIZE = 20000
1148 MAX_RE_SIZE = 20000
1152
1149
1153 def _joinregexes(regexps):
1150 def _joinregexes(regexps):
1154 """gather multiple regular expressions into a single one"""
1151 """gather multiple regular expressions into a single one"""
1155 return '|'.join(regexps)
1152 return '|'.join(regexps)
1156
1153
1157 def _buildregexmatch(kindpats, globsuffix):
1154 def _buildregexmatch(kindpats, globsuffix):
1158 """Build a match function from a list of kinds and kindpats,
1155 """Build a match function from a list of kinds and kindpats,
1159 return regexp string and a matcher function.
1156 return regexp string and a matcher function.
1160
1157
1161 Test too large input
1158 Test too large input
1162 >>> _buildregexmatch([
1159 >>> _buildregexmatch([
1163 ... (b'relglob', b'?' * MAX_RE_SIZE, b'')
1160 ... (b'relglob', b'?' * MAX_RE_SIZE, b'')
1164 ... ], b'$')
1161 ... ], b'$')
1165 Traceback (most recent call last):
1162 Traceback (most recent call last):
1166 ...
1163 ...
1167 Abort: matcher pattern is too long (20009 bytes)
1164 Abort: matcher pattern is too long (20009 bytes)
1168 """
1165 """
1169 try:
1166 try:
1170 allgroups = []
1167 allgroups = []
1171 regexps = [_regex(k, p, globsuffix) for (k, p, s) in kindpats]
1168 regexps = [_regex(k, p, globsuffix) for (k, p, s) in kindpats]
1172 fullregexp = _joinregexes(regexps)
1169 fullregexp = _joinregexes(regexps)
1173
1170
1174 startidx = 0
1171 startidx = 0
1175 groupsize = 0
1172 groupsize = 0
1176 for idx, r in enumerate(regexps):
1173 for idx, r in enumerate(regexps):
1177 piecesize = len(r)
1174 piecesize = len(r)
1178 if piecesize > MAX_RE_SIZE:
1175 if piecesize > MAX_RE_SIZE:
1179 msg = _("matcher pattern is too long (%d bytes)") % piecesize
1176 msg = _("matcher pattern is too long (%d bytes)") % piecesize
1180 raise error.Abort(msg)
1177 raise error.Abort(msg)
1181 elif (groupsize + piecesize) > MAX_RE_SIZE:
1178 elif (groupsize + piecesize) > MAX_RE_SIZE:
1182 group = regexps[startidx:idx]
1179 group = regexps[startidx:idx]
1183 allgroups.append(_joinregexes(group))
1180 allgroups.append(_joinregexes(group))
1184 startidx = idx
1181 startidx = idx
1185 groupsize = 0
1182 groupsize = 0
1186 groupsize += piecesize + 1
1183 groupsize += piecesize + 1
1187
1184
1188 if startidx == 0:
1185 if startidx == 0:
1189 func = _rematcher(fullregexp)
1186 func = _rematcher(fullregexp)
1190 else:
1187 else:
1191 group = regexps[startidx:]
1188 group = regexps[startidx:]
1192 allgroups.append(_joinregexes(group))
1189 allgroups.append(_joinregexes(group))
1193 allmatchers = [_rematcher(g) for g in allgroups]
1190 allmatchers = [_rematcher(g) for g in allgroups]
1194 func = lambda s: any(m(s) for m in allmatchers)
1191 func = lambda s: any(m(s) for m in allmatchers)
1195 return fullregexp, func
1192 return fullregexp, func
1196 except re.error:
1193 except re.error:
1197 for k, p, s in kindpats:
1194 for k, p, s in kindpats:
1198 try:
1195 try:
1199 _rematcher(_regex(k, p, globsuffix))
1196 _rematcher(_regex(k, p, globsuffix))
1200 except re.error:
1197 except re.error:
1201 if s:
1198 if s:
1202 raise error.Abort(_("%s: invalid pattern (%s): %s") %
1199 raise error.Abort(_("%s: invalid pattern (%s): %s") %
1203 (s, k, p))
1200 (s, k, p))
1204 else:
1201 else:
1205 raise error.Abort(_("invalid pattern (%s): %s") % (k, p))
1202 raise error.Abort(_("invalid pattern (%s): %s") % (k, p))
1206 raise error.Abort(_("invalid pattern"))
1203 raise error.Abort(_("invalid pattern"))
1207
1204
1208 def _patternrootsanddirs(kindpats):
1205 def _patternrootsanddirs(kindpats):
1209 '''Returns roots and directories corresponding to each pattern.
1206 '''Returns roots and directories corresponding to each pattern.
1210
1207
1211 This calculates the roots and directories exactly matching the patterns and
1208 This calculates the roots and directories exactly matching the patterns and
1212 returns a tuple of (roots, dirs) for each. It does not return other
1209 returns a tuple of (roots, dirs) for each. It does not return other
1213 directories which may also need to be considered, like the parent
1210 directories which may also need to be considered, like the parent
1214 directories.
1211 directories.
1215 '''
1212 '''
1216 r = []
1213 r = []
1217 d = []
1214 d = []
1218 for kind, pat, source in kindpats:
1215 for kind, pat, source in kindpats:
1219 if kind in ('glob', 'rootglob'): # find the non-glob prefix
1216 if kind in ('glob', 'rootglob'): # find the non-glob prefix
1220 root = []
1217 root = []
1221 for p in pat.split('/'):
1218 for p in pat.split('/'):
1222 if '[' in p or '{' in p or '*' in p or '?' in p:
1219 if '[' in p or '{' in p or '*' in p or '?' in p:
1223 break
1220 break
1224 root.append(p)
1221 root.append(p)
1225 r.append('/'.join(root) or '.')
1222 r.append('/'.join(root) or '.')
1226 elif kind in ('relpath', 'path'):
1223 elif kind in ('relpath', 'path'):
1227 r.append(pat or '.')
1224 r.append(pat or '.')
1228 elif kind in ('rootfilesin',):
1225 elif kind in ('rootfilesin',):
1229 d.append(pat or '.')
1226 d.append(pat or '.')
1230 else: # relglob, re, relre
1227 else: # relglob, re, relre
1231 r.append('.')
1228 r.append('.')
1232 return r, d
1229 return r, d
1233
1230
1234 def _roots(kindpats):
1231 def _roots(kindpats):
1235 '''Returns root directories to match recursively from the given patterns.'''
1232 '''Returns root directories to match recursively from the given patterns.'''
1236 roots, dirs = _patternrootsanddirs(kindpats)
1233 roots, dirs = _patternrootsanddirs(kindpats)
1237 return roots
1234 return roots
1238
1235
1239 def _rootsdirsandparents(kindpats):
1236 def _rootsdirsandparents(kindpats):
1240 '''Returns roots and exact directories from patterns.
1237 '''Returns roots and exact directories from patterns.
1241
1238
1242 `roots` are directories to match recursively, `dirs` should
1239 `roots` are directories to match recursively, `dirs` should
1243 be matched non-recursively, and `parents` are the implicitly required
1240 be matched non-recursively, and `parents` are the implicitly required
1244 directories to walk to items in either roots or dirs.
1241 directories to walk to items in either roots or dirs.
1245
1242
1246 Returns a tuple of (roots, dirs, parents).
1243 Returns a tuple of (roots, dirs, parents).
1247
1244
1248 >>> _rootsdirsandparents(
1245 >>> _rootsdirsandparents(
1249 ... [(b'glob', b'g/h/*', b''), (b'glob', b'g/h', b''),
1246 ... [(b'glob', b'g/h/*', b''), (b'glob', b'g/h', b''),
1250 ... (b'glob', b'g*', b'')])
1247 ... (b'glob', b'g*', b'')])
1251 (['g/h', 'g/h', '.'], [], ['g', '.'])
1248 (['g/h', 'g/h', '.'], [], ['g', '.'])
1252 >>> _rootsdirsandparents(
1249 >>> _rootsdirsandparents(
1253 ... [(b'rootfilesin', b'g/h', b''), (b'rootfilesin', b'', b'')])
1250 ... [(b'rootfilesin', b'g/h', b''), (b'rootfilesin', b'', b'')])
1254 ([], ['g/h', '.'], ['g', '.'])
1251 ([], ['g/h', '.'], ['g', '.'])
1255 >>> _rootsdirsandparents(
1252 >>> _rootsdirsandparents(
1256 ... [(b'relpath', b'r', b''), (b'path', b'p/p', b''),
1253 ... [(b'relpath', b'r', b''), (b'path', b'p/p', b''),
1257 ... (b'path', b'', b'')])
1254 ... (b'path', b'', b'')])
1258 (['r', 'p/p', '.'], [], ['p', '.'])
1255 (['r', 'p/p', '.'], [], ['p', '.'])
1259 >>> _rootsdirsandparents(
1256 >>> _rootsdirsandparents(
1260 ... [(b'relglob', b'rg*', b''), (b're', b're/', b''),
1257 ... [(b'relglob', b'rg*', b''), (b're', b're/', b''),
1261 ... (b'relre', b'rr', b'')])
1258 ... (b'relre', b'rr', b'')])
1262 (['.', '.', '.'], [], ['.'])
1259 (['.', '.', '.'], [], ['.'])
1263 '''
1260 '''
1264 r, d = _patternrootsanddirs(kindpats)
1261 r, d = _patternrootsanddirs(kindpats)
1265
1262
1266 p = []
1263 p = []
1267 # Append the parents as non-recursive/exact directories, since they must be
1264 # Append the parents as non-recursive/exact directories, since they must be
1268 # scanned to get to either the roots or the other exact directories.
1265 # scanned to get to either the roots or the other exact directories.
1269 p.extend(util.dirs(d))
1266 p.extend(util.dirs(d))
1270 p.extend(util.dirs(r))
1267 p.extend(util.dirs(r))
1271 # util.dirs() does not include the root directory, so add it manually
1268 # util.dirs() does not include the root directory, so add it manually
1272 p.append('.')
1269 p.append('.')
1273
1270
1274 # FIXME: all uses of this function convert these to sets, do so before
1271 # FIXME: all uses of this function convert these to sets, do so before
1275 # returning.
1272 # returning.
1276 # FIXME: all uses of this function do not need anything in 'roots' and
1273 # FIXME: all uses of this function do not need anything in 'roots' and
1277 # 'dirs' to also be in 'parents', consider removing them before returning.
1274 # 'dirs' to also be in 'parents', consider removing them before returning.
1278 return r, d, p
1275 return r, d, p
1279
1276
1280 def _explicitfiles(kindpats):
1277 def _explicitfiles(kindpats):
1281 '''Returns the potential explicit filenames from the patterns.
1278 '''Returns the potential explicit filenames from the patterns.
1282
1279
1283 >>> _explicitfiles([(b'path', b'foo/bar', b'')])
1280 >>> _explicitfiles([(b'path', b'foo/bar', b'')])
1284 ['foo/bar']
1281 ['foo/bar']
1285 >>> _explicitfiles([(b'rootfilesin', b'foo/bar', b'')])
1282 >>> _explicitfiles([(b'rootfilesin', b'foo/bar', b'')])
1286 []
1283 []
1287 '''
1284 '''
1288 # Keep only the pattern kinds where one can specify filenames (vs only
1285 # Keep only the pattern kinds where one can specify filenames (vs only
1289 # directory names).
1286 # directory names).
1290 filable = [kp for kp in kindpats if kp[0] not in ('rootfilesin',)]
1287 filable = [kp for kp in kindpats if kp[0] not in ('rootfilesin',)]
1291 return _roots(filable)
1288 return _roots(filable)
1292
1289
1293 def _prefix(kindpats):
1290 def _prefix(kindpats):
1294 '''Whether all the patterns match a prefix (i.e. recursively)'''
1291 '''Whether all the patterns match a prefix (i.e. recursively)'''
1295 for kind, pat, source in kindpats:
1292 for kind, pat, source in kindpats:
1296 if kind not in ('path', 'relpath'):
1293 if kind not in ('path', 'relpath'):
1297 return False
1294 return False
1298 return True
1295 return True
1299
1296
1300 _commentre = None
1297 _commentre = None
1301
1298
1302 def readpatternfile(filepath, warn, sourceinfo=False):
1299 def readpatternfile(filepath, warn, sourceinfo=False):
1303 '''parse a pattern file, returning a list of
1300 '''parse a pattern file, returning a list of
1304 patterns. These patterns should be given to compile()
1301 patterns. These patterns should be given to compile()
1305 to be validated and converted into a match function.
1302 to be validated and converted into a match function.
1306
1303
1307 trailing white space is dropped.
1304 trailing white space is dropped.
1308 the escape character is backslash.
1305 the escape character is backslash.
1309 comments start with #.
1306 comments start with #.
1310 empty lines are skipped.
1307 empty lines are skipped.
1311
1308
1312 lines can be of the following formats:
1309 lines can be of the following formats:
1313
1310
1314 syntax: regexp # defaults following lines to non-rooted regexps
1311 syntax: regexp # defaults following lines to non-rooted regexps
1315 syntax: glob # defaults following lines to non-rooted globs
1312 syntax: glob # defaults following lines to non-rooted globs
1316 re:pattern # non-rooted regular expression
1313 re:pattern # non-rooted regular expression
1317 glob:pattern # non-rooted glob
1314 glob:pattern # non-rooted glob
1318 rootglob:pat # rooted glob (same root as ^ in regexps)
1315 rootglob:pat # rooted glob (same root as ^ in regexps)
1319 pattern # pattern of the current default type
1316 pattern # pattern of the current default type
1320
1317
1321 if sourceinfo is set, returns a list of tuples:
1318 if sourceinfo is set, returns a list of tuples:
1322 (pattern, lineno, originalline). This is useful to debug ignore patterns.
1319 (pattern, lineno, originalline). This is useful to debug ignore patterns.
1323 '''
1320 '''
1324
1321
1325 syntaxes = {
1322 syntaxes = {
1326 're': 'relre:',
1323 're': 'relre:',
1327 'regexp': 'relre:',
1324 'regexp': 'relre:',
1328 'glob': 'relglob:',
1325 'glob': 'relglob:',
1329 'rootglob': 'rootglob:',
1326 'rootglob': 'rootglob:',
1330 'include': 'include',
1327 'include': 'include',
1331 'subinclude': 'subinclude',
1328 'subinclude': 'subinclude',
1332 }
1329 }
1333 syntax = 'relre:'
1330 syntax = 'relre:'
1334 patterns = []
1331 patterns = []
1335
1332
1336 fp = open(filepath, 'rb')
1333 fp = open(filepath, 'rb')
1337 for lineno, line in enumerate(util.iterfile(fp), start=1):
1334 for lineno, line in enumerate(util.iterfile(fp), start=1):
1338 if "#" in line:
1335 if "#" in line:
1339 global _commentre
1336 global _commentre
1340 if not _commentre:
1337 if not _commentre:
1341 _commentre = util.re.compile(br'((?:^|[^\\])(?:\\\\)*)#.*')
1338 _commentre = util.re.compile(br'((?:^|[^\\])(?:\\\\)*)#.*')
1342 # remove comments prefixed by an even number of escapes
1339 # remove comments prefixed by an even number of escapes
1343 m = _commentre.search(line)
1340 m = _commentre.search(line)
1344 if m:
1341 if m:
1345 line = line[:m.end(1)]
1342 line = line[:m.end(1)]
1346 # fixup properly escaped comments that survived the above
1343 # fixup properly escaped comments that survived the above
1347 line = line.replace("\\#", "#")
1344 line = line.replace("\\#", "#")
1348 line = line.rstrip()
1345 line = line.rstrip()
1349 if not line:
1346 if not line:
1350 continue
1347 continue
1351
1348
1352 if line.startswith('syntax:'):
1349 if line.startswith('syntax:'):
1353 s = line[7:].strip()
1350 s = line[7:].strip()
1354 try:
1351 try:
1355 syntax = syntaxes[s]
1352 syntax = syntaxes[s]
1356 except KeyError:
1353 except KeyError:
1357 if warn:
1354 if warn:
1358 warn(_("%s: ignoring invalid syntax '%s'\n") %
1355 warn(_("%s: ignoring invalid syntax '%s'\n") %
1359 (filepath, s))
1356 (filepath, s))
1360 continue
1357 continue
1361
1358
1362 linesyntax = syntax
1359 linesyntax = syntax
1363 for s, rels in syntaxes.iteritems():
1360 for s, rels in syntaxes.iteritems():
1364 if line.startswith(rels):
1361 if line.startswith(rels):
1365 linesyntax = rels
1362 linesyntax = rels
1366 line = line[len(rels):]
1363 line = line[len(rels):]
1367 break
1364 break
1368 elif line.startswith(s+':'):
1365 elif line.startswith(s+':'):
1369 linesyntax = rels
1366 linesyntax = rels
1370 line = line[len(s) + 1:]
1367 line = line[len(s) + 1:]
1371 break
1368 break
1372 if sourceinfo:
1369 if sourceinfo:
1373 patterns.append((linesyntax + line, lineno, line))
1370 patterns.append((linesyntax + line, lineno, line))
1374 else:
1371 else:
1375 patterns.append(linesyntax + line)
1372 patterns.append(linesyntax + line)
1376 fp.close()
1373 fp.close()
1377 return patterns
1374 return patterns
General Comments 0
You need to be logged in to leave comments. Login now