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