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