##// END OF EJS Templates
match: enable 'subinclude:' syntax...
Durham Goode -
r25283:19d0e5ef default
parent child Browse files
Show More
@@ -1,620 +1,655 b''
1 # match.py - filename matching
1 # match.py - filename matching
2 #
2 #
3 # Copyright 2008, 2009 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2008, 2009 Matt Mackall <mpm@selenic.com> and others
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 import re
8 import re
9 import util, pathutil
9 import util, pathutil
10 from i18n import _
10 from i18n import _
11
11
12 propertycache = util.propertycache
12 propertycache = util.propertycache
13
13
14 def _rematcher(regex):
14 def _rematcher(regex):
15 '''compile the regexp with the best available regexp engine and return a
15 '''compile the regexp with the best available regexp engine and return a
16 matcher function'''
16 matcher function'''
17 m = util.re.compile(regex)
17 m = util.re.compile(regex)
18 try:
18 try:
19 # slightly faster, provided by facebook's re2 bindings
19 # slightly faster, provided by facebook's re2 bindings
20 return m.test_match
20 return m.test_match
21 except AttributeError:
21 except AttributeError:
22 return m.match
22 return m.match
23
23
24 def _expandsets(kindpats, ctx, listsubrepos):
24 def _expandsets(kindpats, ctx, listsubrepos):
25 '''Returns the kindpats list with the 'set' patterns expanded.'''
25 '''Returns the kindpats list with the 'set' patterns expanded.'''
26 fset = set()
26 fset = set()
27 other = []
27 other = []
28
28
29 for kind, pat, source in kindpats:
29 for kind, pat, source in kindpats:
30 if kind == 'set':
30 if kind == 'set':
31 if not ctx:
31 if not ctx:
32 raise util.Abort("fileset expression with no context")
32 raise util.Abort("fileset expression with no context")
33 s = ctx.getfileset(pat)
33 s = ctx.getfileset(pat)
34 fset.update(s)
34 fset.update(s)
35
35
36 if listsubrepos:
36 if listsubrepos:
37 for subpath in ctx.substate:
37 for subpath in ctx.substate:
38 s = ctx.sub(subpath).getfileset(pat)
38 s = ctx.sub(subpath).getfileset(pat)
39 fset.update(subpath + '/' + f for f in s)
39 fset.update(subpath + '/' + f for f in s)
40
40
41 continue
41 continue
42 other.append((kind, pat, source))
42 other.append((kind, pat, source))
43 return fset, other
43 return fset, other
44
44
45 def _expandsubinclude(kindpats, root):
46 '''Returns the list of subinclude matchers and the kindpats without the
47 subincludes in it.'''
48 relmatchers = []
49 other = []
50
51 for kind, pat, source in kindpats:
52 if kind == 'subinclude':
53 sourceroot = pathutil.dirname(source)
54 pat = util.pconvert(pat)
55 path = pathutil.join(sourceroot, pat)
56
57 newroot = pathutil.dirname(path)
58 relmatcher = match(newroot, '', [], ['include:%s' % path])
59
60 prefix = pathutil.canonpath(root, root, newroot)
61 if prefix:
62 prefix += '/'
63 relmatchers.append((prefix, relmatcher))
64 else:
65 other.append((kind, pat, source))
66
67 return relmatchers, other
68
45 def _kindpatsalwaysmatch(kindpats):
69 def _kindpatsalwaysmatch(kindpats):
46 """"Checks whether the kindspats match everything, as e.g.
70 """"Checks whether the kindspats match everything, as e.g.
47 'relpath:.' does.
71 'relpath:.' does.
48 """
72 """
49 for kind, pat, source in kindpats:
73 for kind, pat, source in kindpats:
50 if pat != '' or kind not in ['relpath', 'glob']:
74 if pat != '' or kind not in ['relpath', 'glob']:
51 return False
75 return False
52 return True
76 return True
53
77
54 class match(object):
78 class match(object):
55 def __init__(self, root, cwd, patterns, include=[], exclude=[],
79 def __init__(self, root, cwd, patterns, include=[], exclude=[],
56 default='glob', exact=False, auditor=None, ctx=None,
80 default='glob', exact=False, auditor=None, ctx=None,
57 listsubrepos=False, warn=None):
81 listsubrepos=False, warn=None):
58 """build an object to match a set of file patterns
82 """build an object to match a set of file patterns
59
83
60 arguments:
84 arguments:
61 root - the canonical root of the tree you're matching against
85 root - the canonical root of the tree you're matching against
62 cwd - the current working directory, if relevant
86 cwd - the current working directory, if relevant
63 patterns - patterns to find
87 patterns - patterns to find
64 include - patterns to include (unless they are excluded)
88 include - patterns to include (unless they are excluded)
65 exclude - patterns to exclude (even if they are included)
89 exclude - patterns to exclude (even if they are included)
66 default - if a pattern in patterns has no explicit type, assume this one
90 default - if a pattern in patterns has no explicit type, assume this one
67 exact - patterns are actually filenames (include/exclude still apply)
91 exact - patterns are actually filenames (include/exclude still apply)
68 warn - optional function used for printing warnings
92 warn - optional function used for printing warnings
69
93
70 a pattern is one of:
94 a pattern is one of:
71 'glob:<glob>' - a glob relative to cwd
95 'glob:<glob>' - a glob relative to cwd
72 're:<regexp>' - a regular expression
96 're:<regexp>' - a regular expression
73 'path:<path>' - a path relative to repository root
97 'path:<path>' - a path relative to repository root
74 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
98 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
75 'relpath:<path>' - a path relative to cwd
99 'relpath:<path>' - a path relative to cwd
76 'relre:<regexp>' - a regexp that needn't match the start of a name
100 'relre:<regexp>' - a regexp that needn't match the start of a name
77 'set:<fileset>' - a fileset expression
101 'set:<fileset>' - a fileset expression
78 'include:<path>' - a file of patterns to read and include
102 'include:<path>' - a file of patterns to read and include
103 'subinclude:<path>' - a file of patterns to match against files under
104 the same directory
79 '<something>' - a pattern of the specified default type
105 '<something>' - a pattern of the specified default type
80 """
106 """
81
107
82 self._root = root
108 self._root = root
83 self._cwd = cwd
109 self._cwd = cwd
84 self._files = [] # exact files and roots of patterns
110 self._files = [] # exact files and roots of patterns
85 self._anypats = bool(include or exclude)
111 self._anypats = bool(include or exclude)
86 self._always = False
112 self._always = False
87 self._pathrestricted = bool(include or exclude or patterns)
113 self._pathrestricted = bool(include or exclude or patterns)
88 self._warn = warn
114 self._warn = warn
89 self._includeroots = set()
115 self._includeroots = set()
90 self._includedirs = set(['.'])
116 self._includedirs = set(['.'])
91 self._excluderoots = set()
117 self._excluderoots = set()
92
118
93 matchfns = []
119 matchfns = []
94 if include:
120 if include:
95 kindpats = self._normalize(include, 'glob', root, cwd, auditor)
121 kindpats = self._normalize(include, 'glob', root, cwd, auditor)
96 self.includepat, im = _buildmatch(ctx, kindpats, '(?:/|$)',
122 self.includepat, im = _buildmatch(ctx, kindpats, '(?:/|$)',
97 listsubrepos, root)
123 listsubrepos, root)
98 self._includeroots.update(_roots(kindpats))
124 self._includeroots.update(_roots(kindpats))
99 self._includeroots.discard('.')
125 self._includeroots.discard('.')
100 self._includedirs.update(util.dirs(self._includeroots))
126 self._includedirs.update(util.dirs(self._includeroots))
101 matchfns.append(im)
127 matchfns.append(im)
102 if exclude:
128 if exclude:
103 kindpats = self._normalize(exclude, 'glob', root, cwd, auditor)
129 kindpats = self._normalize(exclude, 'glob', root, cwd, auditor)
104 self.excludepat, em = _buildmatch(ctx, kindpats, '(?:/|$)',
130 self.excludepat, em = _buildmatch(ctx, kindpats, '(?:/|$)',
105 listsubrepos, root)
131 listsubrepos, root)
106 self._excluderoots.update(_roots(kindpats))
132 self._excluderoots.update(_roots(kindpats))
107 self._excluderoots.discard('.')
133 self._excluderoots.discard('.')
108 matchfns.append(lambda f: not em(f))
134 matchfns.append(lambda f: not em(f))
109 if exact:
135 if exact:
110 if isinstance(patterns, list):
136 if isinstance(patterns, list):
111 self._files = patterns
137 self._files = patterns
112 else:
138 else:
113 self._files = list(patterns)
139 self._files = list(patterns)
114 matchfns.append(self.exact)
140 matchfns.append(self.exact)
115 elif patterns:
141 elif patterns:
116 kindpats = self._normalize(patterns, default, root, cwd, auditor)
142 kindpats = self._normalize(patterns, default, root, cwd, auditor)
117 if not _kindpatsalwaysmatch(kindpats):
143 if not _kindpatsalwaysmatch(kindpats):
118 self._files = _roots(kindpats)
144 self._files = _roots(kindpats)
119 self._anypats = self._anypats or _anypats(kindpats)
145 self._anypats = self._anypats or _anypats(kindpats)
120 self.patternspat, pm = _buildmatch(ctx, kindpats, '$',
146 self.patternspat, pm = _buildmatch(ctx, kindpats, '$',
121 listsubrepos, root)
147 listsubrepos, root)
122 matchfns.append(pm)
148 matchfns.append(pm)
123
149
124 if not matchfns:
150 if not matchfns:
125 m = util.always
151 m = util.always
126 self._always = True
152 self._always = True
127 elif len(matchfns) == 1:
153 elif len(matchfns) == 1:
128 m = matchfns[0]
154 m = matchfns[0]
129 else:
155 else:
130 def m(f):
156 def m(f):
131 for matchfn in matchfns:
157 for matchfn in matchfns:
132 if not matchfn(f):
158 if not matchfn(f):
133 return False
159 return False
134 return True
160 return True
135
161
136 self.matchfn = m
162 self.matchfn = m
137 self._fileroots = set(self._files)
163 self._fileroots = set(self._files)
138
164
139 def __call__(self, fn):
165 def __call__(self, fn):
140 return self.matchfn(fn)
166 return self.matchfn(fn)
141 def __iter__(self):
167 def __iter__(self):
142 for f in self._files:
168 for f in self._files:
143 yield f
169 yield f
144
170
145 # Callbacks related to how the matcher is used by dirstate.walk.
171 # Callbacks related to how the matcher is used by dirstate.walk.
146 # Subscribers to these events must monkeypatch the matcher object.
172 # Subscribers to these events must monkeypatch the matcher object.
147 def bad(self, f, msg):
173 def bad(self, f, msg):
148 '''Callback from dirstate.walk for each explicit file that can't be
174 '''Callback from dirstate.walk for each explicit file that can't be
149 found/accessed, with an error message.'''
175 found/accessed, with an error message.'''
150 pass
176 pass
151
177
152 # If an explicitdir is set, it will be called when an explicitly listed
178 # If an explicitdir is set, it will be called when an explicitly listed
153 # directory is visited.
179 # directory is visited.
154 explicitdir = None
180 explicitdir = None
155
181
156 # If an traversedir is set, it will be called when a directory discovered
182 # If an traversedir is set, it will be called when a directory discovered
157 # by recursive traversal is visited.
183 # by recursive traversal is visited.
158 traversedir = None
184 traversedir = None
159
185
160 def abs(self, f):
186 def abs(self, f):
161 '''Convert a repo path back to path that is relative to the root of the
187 '''Convert a repo path back to path that is relative to the root of the
162 matcher.'''
188 matcher.'''
163 return f
189 return f
164
190
165 def rel(self, f):
191 def rel(self, f):
166 '''Convert repo path back to path that is relative to cwd of matcher.'''
192 '''Convert repo path back to path that is relative to cwd of matcher.'''
167 return util.pathto(self._root, self._cwd, f)
193 return util.pathto(self._root, self._cwd, f)
168
194
169 def uipath(self, f):
195 def uipath(self, f):
170 '''Convert repo path to a display path. If patterns or -I/-X were used
196 '''Convert repo path to a display path. If patterns or -I/-X were used
171 to create this matcher, the display path will be relative to cwd.
197 to create this matcher, the display path will be relative to cwd.
172 Otherwise it is relative to the root of the repo.'''
198 Otherwise it is relative to the root of the repo.'''
173 return (self._pathrestricted and self.rel(f)) or self.abs(f)
199 return (self._pathrestricted and self.rel(f)) or self.abs(f)
174
200
175 def files(self):
201 def files(self):
176 '''Explicitly listed files or patterns or roots:
202 '''Explicitly listed files or patterns or roots:
177 if no patterns or .always(): empty list,
203 if no patterns or .always(): empty list,
178 if exact: list exact files,
204 if exact: list exact files,
179 if not .anypats(): list all files and dirs,
205 if not .anypats(): list all files and dirs,
180 else: optimal roots'''
206 else: optimal roots'''
181 return self._files
207 return self._files
182
208
183 @propertycache
209 @propertycache
184 def _dirs(self):
210 def _dirs(self):
185 return set(util.dirs(self._fileroots)) | set(['.'])
211 return set(util.dirs(self._fileroots)) | set(['.'])
186
212
187 def visitdir(self, dir):
213 def visitdir(self, dir):
188 '''Decides whether a directory should be visited based on whether it
214 '''Decides whether a directory should be visited based on whether it
189 has potential matches in it or one of its subdirectories. This is
215 has potential matches in it or one of its subdirectories. This is
190 based on the match's primary, included, and excluded patterns.
216 based on the match's primary, included, and excluded patterns.
191
217
192 This function's behavior is undefined if it has returned False for
218 This function's behavior is undefined if it has returned False for
193 one of the dir's parent directories.
219 one of the dir's parent directories.
194 '''
220 '''
195 if dir in self._excluderoots:
221 if dir in self._excluderoots:
196 return False
222 return False
197 parentdirs = None
223 parentdirs = None
198 if (self._includeroots and dir not in self._includeroots and
224 if (self._includeroots and dir not in self._includeroots and
199 dir not in self._includedirs):
225 dir not in self._includedirs):
200 parentdirs = list(util.finddirs(dir))
226 parentdirs = list(util.finddirs(dir))
201 if not any(parent in self._includeroots for parent in parentdirs):
227 if not any(parent in self._includeroots for parent in parentdirs):
202 return False
228 return False
203 return (not self._fileroots or '.' in self._fileroots or
229 return (not self._fileroots or '.' in self._fileroots or
204 dir in self._fileroots or dir in self._dirs or
230 dir in self._fileroots or dir in self._dirs or
205 any(parentdir in self._fileroots
231 any(parentdir in self._fileroots
206 for parentdir in parentdirs or util.finddirs(dir)))
232 for parentdir in parentdirs or util.finddirs(dir)))
207
233
208 def exact(self, f):
234 def exact(self, f):
209 '''Returns True if f is in .files().'''
235 '''Returns True if f is in .files().'''
210 return f in self._fileroots
236 return f in self._fileroots
211
237
212 def anypats(self):
238 def anypats(self):
213 '''Matcher uses patterns or include/exclude.'''
239 '''Matcher uses patterns or include/exclude.'''
214 return self._anypats
240 return self._anypats
215
241
216 def always(self):
242 def always(self):
217 '''Matcher will match everything and .files() will be empty
243 '''Matcher will match everything and .files() will be empty
218 - optimization might be possible and necessary.'''
244 - optimization might be possible and necessary.'''
219 return self._always
245 return self._always
220
246
221 def ispartial(self):
247 def ispartial(self):
222 '''True if the matcher won't always match.
248 '''True if the matcher won't always match.
223
249
224 Although it's just the inverse of _always in this implementation,
250 Although it's just the inverse of _always in this implementation,
225 an extenion such as narrowhg might make it return something
251 an extenion such as narrowhg might make it return something
226 slightly different.'''
252 slightly different.'''
227 return not self._always
253 return not self._always
228
254
229 def isexact(self):
255 def isexact(self):
230 return self.matchfn == self.exact
256 return self.matchfn == self.exact
231
257
232 def prefix(self):
258 def prefix(self):
233 return not self.always() and not self.isexact() and not self.anypats()
259 return not self.always() and not self.isexact() and not self.anypats()
234
260
235 def _normalize(self, patterns, default, root, cwd, auditor):
261 def _normalize(self, patterns, default, root, cwd, auditor):
236 '''Convert 'kind:pat' from the patterns list to tuples with kind and
262 '''Convert 'kind:pat' from the patterns list to tuples with kind and
237 normalized and rooted patterns and with listfiles expanded.'''
263 normalized and rooted patterns and with listfiles expanded.'''
238 kindpats = []
264 kindpats = []
239 for kind, pat in [_patsplit(p, default) for p in patterns]:
265 for kind, pat in [_patsplit(p, default) for p in patterns]:
240 if kind in ('glob', 'relpath'):
266 if kind in ('glob', 'relpath'):
241 pat = pathutil.canonpath(root, cwd, pat, auditor)
267 pat = pathutil.canonpath(root, cwd, pat, auditor)
242 elif kind in ('relglob', 'path'):
268 elif kind in ('relglob', 'path'):
243 pat = util.normpath(pat)
269 pat = util.normpath(pat)
244 elif kind in ('listfile', 'listfile0'):
270 elif kind in ('listfile', 'listfile0'):
245 try:
271 try:
246 files = util.readfile(pat)
272 files = util.readfile(pat)
247 if kind == 'listfile0':
273 if kind == 'listfile0':
248 files = files.split('\0')
274 files = files.split('\0')
249 else:
275 else:
250 files = files.splitlines()
276 files = files.splitlines()
251 files = [f for f in files if f]
277 files = [f for f in files if f]
252 except EnvironmentError:
278 except EnvironmentError:
253 raise util.Abort(_("unable to read file list (%s)") % pat)
279 raise util.Abort(_("unable to read file list (%s)") % pat)
254 for k, p, source in self._normalize(files, default, root, cwd,
280 for k, p, source in self._normalize(files, default, root, cwd,
255 auditor):
281 auditor):
256 kindpats.append((k, p, pat))
282 kindpats.append((k, p, pat))
257 continue
283 continue
258 elif kind == 'include':
284 elif kind == 'include':
259 try:
285 try:
260 includepats = readpatternfile(pat, self._warn)
286 includepats = readpatternfile(pat, self._warn)
261 for k, p, source in self._normalize(includepats, default,
287 for k, p, source in self._normalize(includepats, default,
262 root, cwd, auditor):
288 root, cwd, auditor):
263 kindpats.append((k, p, source or pat))
289 kindpats.append((k, p, source or pat))
264 except util.Abort, inst:
290 except util.Abort, inst:
265 raise util.Abort('%s: %s' % (pat, inst[0]))
291 raise util.Abort('%s: %s' % (pat, inst[0]))
266 except IOError, inst:
292 except IOError, inst:
267 if self._warn:
293 if self._warn:
268 self._warn(_("skipping unreadable pattern file "
294 self._warn(_("skipping unreadable pattern file "
269 "'%s': %s\n") % (pat, inst.strerror))
295 "'%s': %s\n") % (pat, inst.strerror))
270 continue
296 continue
271 # else: re or relre - which cannot be normalized
297 # else: re or relre - which cannot be normalized
272 kindpats.append((kind, pat, ''))
298 kindpats.append((kind, pat, ''))
273 return kindpats
299 return kindpats
274
300
275 def exact(root, cwd, files):
301 def exact(root, cwd, files):
276 return match(root, cwd, files, exact=True)
302 return match(root, cwd, files, exact=True)
277
303
278 def always(root, cwd):
304 def always(root, cwd):
279 return match(root, cwd, [])
305 return match(root, cwd, [])
280
306
281 class narrowmatcher(match):
307 class narrowmatcher(match):
282 """Adapt a matcher to work on a subdirectory only.
308 """Adapt a matcher to work on a subdirectory only.
283
309
284 The paths are remapped to remove/insert the path as needed:
310 The paths are remapped to remove/insert the path as needed:
285
311
286 >>> m1 = match('root', '', ['a.txt', 'sub/b.txt'])
312 >>> m1 = match('root', '', ['a.txt', 'sub/b.txt'])
287 >>> m2 = narrowmatcher('sub', m1)
313 >>> m2 = narrowmatcher('sub', m1)
288 >>> bool(m2('a.txt'))
314 >>> bool(m2('a.txt'))
289 False
315 False
290 >>> bool(m2('b.txt'))
316 >>> bool(m2('b.txt'))
291 True
317 True
292 >>> bool(m2.matchfn('a.txt'))
318 >>> bool(m2.matchfn('a.txt'))
293 False
319 False
294 >>> bool(m2.matchfn('b.txt'))
320 >>> bool(m2.matchfn('b.txt'))
295 True
321 True
296 >>> m2.files()
322 >>> m2.files()
297 ['b.txt']
323 ['b.txt']
298 >>> m2.exact('b.txt')
324 >>> m2.exact('b.txt')
299 True
325 True
300 >>> util.pconvert(m2.rel('b.txt'))
326 >>> util.pconvert(m2.rel('b.txt'))
301 'sub/b.txt'
327 'sub/b.txt'
302 >>> def bad(f, msg):
328 >>> def bad(f, msg):
303 ... print "%s: %s" % (f, msg)
329 ... print "%s: %s" % (f, msg)
304 >>> m1.bad = bad
330 >>> m1.bad = bad
305 >>> m2.bad('x.txt', 'No such file')
331 >>> m2.bad('x.txt', 'No such file')
306 sub/x.txt: No such file
332 sub/x.txt: No such file
307 >>> m2.abs('c.txt')
333 >>> m2.abs('c.txt')
308 'sub/c.txt'
334 'sub/c.txt'
309 """
335 """
310
336
311 def __init__(self, path, matcher):
337 def __init__(self, path, matcher):
312 self._root = matcher._root
338 self._root = matcher._root
313 self._cwd = matcher._cwd
339 self._cwd = matcher._cwd
314 self._path = path
340 self._path = path
315 self._matcher = matcher
341 self._matcher = matcher
316 self._always = matcher._always
342 self._always = matcher._always
317 self._pathrestricted = matcher._pathrestricted
343 self._pathrestricted = matcher._pathrestricted
318
344
319 self._files = [f[len(path) + 1:] for f in matcher._files
345 self._files = [f[len(path) + 1:] for f in matcher._files
320 if f.startswith(path + "/")]
346 if f.startswith(path + "/")]
321
347
322 # If the parent repo had a path to this subrepo and no patterns are
348 # If the parent repo had a path to this subrepo and no patterns are
323 # specified, this submatcher always matches.
349 # specified, this submatcher always matches.
324 if not self._always and not matcher._anypats:
350 if not self._always and not matcher._anypats:
325 self._always = any(f == path for f in matcher._files)
351 self._always = any(f == path for f in matcher._files)
326
352
327 self._anypats = matcher._anypats
353 self._anypats = matcher._anypats
328 self.matchfn = lambda fn: matcher.matchfn(self._path + "/" + fn)
354 self.matchfn = lambda fn: matcher.matchfn(self._path + "/" + fn)
329 self._fileroots = set(self._files)
355 self._fileroots = set(self._files)
330
356
331 def abs(self, f):
357 def abs(self, f):
332 return self._matcher.abs(self._path + "/" + f)
358 return self._matcher.abs(self._path + "/" + f)
333
359
334 def bad(self, f, msg):
360 def bad(self, f, msg):
335 self._matcher.bad(self._path + "/" + f, msg)
361 self._matcher.bad(self._path + "/" + f, msg)
336
362
337 def rel(self, f):
363 def rel(self, f):
338 return self._matcher.rel(self._path + "/" + f)
364 return self._matcher.rel(self._path + "/" + f)
339
365
340 class icasefsmatcher(match):
366 class icasefsmatcher(match):
341 """A matcher for wdir on case insensitive filesystems, which normalizes the
367 """A matcher for wdir on case insensitive filesystems, which normalizes the
342 given patterns to the case in the filesystem.
368 given patterns to the case in the filesystem.
343 """
369 """
344
370
345 def __init__(self, root, cwd, patterns, include, exclude, default, auditor,
371 def __init__(self, root, cwd, patterns, include, exclude, default, auditor,
346 ctx, listsubrepos=False):
372 ctx, listsubrepos=False):
347 init = super(icasefsmatcher, self).__init__
373 init = super(icasefsmatcher, self).__init__
348 self._dsnormalize = ctx.repo().dirstate.normalize
374 self._dsnormalize = ctx.repo().dirstate.normalize
349
375
350 init(root, cwd, patterns, include, exclude, default, auditor=auditor,
376 init(root, cwd, patterns, include, exclude, default, auditor=auditor,
351 ctx=ctx, listsubrepos=listsubrepos)
377 ctx=ctx, listsubrepos=listsubrepos)
352
378
353 # m.exact(file) must be based off of the actual user input, otherwise
379 # m.exact(file) must be based off of the actual user input, otherwise
354 # inexact case matches are treated as exact, and not noted without -v.
380 # inexact case matches are treated as exact, and not noted without -v.
355 if self._files:
381 if self._files:
356 self._fileroots = set(_roots(self._kp))
382 self._fileroots = set(_roots(self._kp))
357
383
358 def _normalize(self, patterns, default, root, cwd, auditor):
384 def _normalize(self, patterns, default, root, cwd, auditor):
359 self._kp = super(icasefsmatcher, self)._normalize(patterns, default,
385 self._kp = super(icasefsmatcher, self)._normalize(patterns, default,
360 root, cwd, auditor)
386 root, cwd, auditor)
361 kindpats = []
387 kindpats = []
362 for kind, pats, source in self._kp:
388 for kind, pats, source in self._kp:
363 if kind not in ('re', 'relre'): # regex can't be normalized
389 if kind not in ('re', 'relre'): # regex can't be normalized
364 pats = self._dsnormalize(pats)
390 pats = self._dsnormalize(pats)
365 kindpats.append((kind, pats, source))
391 kindpats.append((kind, pats, source))
366 return kindpats
392 return kindpats
367
393
368 def patkind(pattern, default=None):
394 def patkind(pattern, default=None):
369 '''If pattern is 'kind:pat' with a known kind, return kind.'''
395 '''If pattern is 'kind:pat' with a known kind, return kind.'''
370 return _patsplit(pattern, default)[0]
396 return _patsplit(pattern, default)[0]
371
397
372 def _patsplit(pattern, default):
398 def _patsplit(pattern, default):
373 """Split a string into the optional pattern kind prefix and the actual
399 """Split a string into the optional pattern kind prefix and the actual
374 pattern."""
400 pattern."""
375 if ':' in pattern:
401 if ':' in pattern:
376 kind, pat = pattern.split(':', 1)
402 kind, pat = pattern.split(':', 1)
377 if kind in ('re', 'glob', 'path', 'relglob', 'relpath', 'relre',
403 if kind in ('re', 'glob', 'path', 'relglob', 'relpath', 'relre',
378 'listfile', 'listfile0', 'set', 'include'):
404 'listfile', 'listfile0', 'set', 'include', 'subinclude'):
379 return kind, pat
405 return kind, pat
380 return default, pattern
406 return default, pattern
381
407
382 def _globre(pat):
408 def _globre(pat):
383 r'''Convert an extended glob string to a regexp string.
409 r'''Convert an extended glob string to a regexp string.
384
410
385 >>> print _globre(r'?')
411 >>> print _globre(r'?')
386 .
412 .
387 >>> print _globre(r'*')
413 >>> print _globre(r'*')
388 [^/]*
414 [^/]*
389 >>> print _globre(r'**')
415 >>> print _globre(r'**')
390 .*
416 .*
391 >>> print _globre(r'**/a')
417 >>> print _globre(r'**/a')
392 (?:.*/)?a
418 (?:.*/)?a
393 >>> print _globre(r'a/**/b')
419 >>> print _globre(r'a/**/b')
394 a\/(?:.*/)?b
420 a\/(?:.*/)?b
395 >>> print _globre(r'[a*?!^][^b][!c]')
421 >>> print _globre(r'[a*?!^][^b][!c]')
396 [a*?!^][\^b][^c]
422 [a*?!^][\^b][^c]
397 >>> print _globre(r'{a,b}')
423 >>> print _globre(r'{a,b}')
398 (?:a|b)
424 (?:a|b)
399 >>> print _globre(r'.\*\?')
425 >>> print _globre(r'.\*\?')
400 \.\*\?
426 \.\*\?
401 '''
427 '''
402 i, n = 0, len(pat)
428 i, n = 0, len(pat)
403 res = ''
429 res = ''
404 group = 0
430 group = 0
405 escape = util.re.escape
431 escape = util.re.escape
406 def peek():
432 def peek():
407 return i < n and pat[i]
433 return i < n and pat[i]
408 while i < n:
434 while i < n:
409 c = pat[i]
435 c = pat[i]
410 i += 1
436 i += 1
411 if c not in '*?[{},\\':
437 if c not in '*?[{},\\':
412 res += escape(c)
438 res += escape(c)
413 elif c == '*':
439 elif c == '*':
414 if peek() == '*':
440 if peek() == '*':
415 i += 1
441 i += 1
416 if peek() == '/':
442 if peek() == '/':
417 i += 1
443 i += 1
418 res += '(?:.*/)?'
444 res += '(?:.*/)?'
419 else:
445 else:
420 res += '.*'
446 res += '.*'
421 else:
447 else:
422 res += '[^/]*'
448 res += '[^/]*'
423 elif c == '?':
449 elif c == '?':
424 res += '.'
450 res += '.'
425 elif c == '[':
451 elif c == '[':
426 j = i
452 j = i
427 if j < n and pat[j] in '!]':
453 if j < n and pat[j] in '!]':
428 j += 1
454 j += 1
429 while j < n and pat[j] != ']':
455 while j < n and pat[j] != ']':
430 j += 1
456 j += 1
431 if j >= n:
457 if j >= n:
432 res += '\\['
458 res += '\\['
433 else:
459 else:
434 stuff = pat[i:j].replace('\\','\\\\')
460 stuff = pat[i:j].replace('\\','\\\\')
435 i = j + 1
461 i = j + 1
436 if stuff[0] == '!':
462 if stuff[0] == '!':
437 stuff = '^' + stuff[1:]
463 stuff = '^' + stuff[1:]
438 elif stuff[0] == '^':
464 elif stuff[0] == '^':
439 stuff = '\\' + stuff
465 stuff = '\\' + stuff
440 res = '%s[%s]' % (res, stuff)
466 res = '%s[%s]' % (res, stuff)
441 elif c == '{':
467 elif c == '{':
442 group += 1
468 group += 1
443 res += '(?:'
469 res += '(?:'
444 elif c == '}' and group:
470 elif c == '}' and group:
445 res += ')'
471 res += ')'
446 group -= 1
472 group -= 1
447 elif c == ',' and group:
473 elif c == ',' and group:
448 res += '|'
474 res += '|'
449 elif c == '\\':
475 elif c == '\\':
450 p = peek()
476 p = peek()
451 if p:
477 if p:
452 i += 1
478 i += 1
453 res += escape(p)
479 res += escape(p)
454 else:
480 else:
455 res += escape(c)
481 res += escape(c)
456 else:
482 else:
457 res += escape(c)
483 res += escape(c)
458 return res
484 return res
459
485
460 def _regex(kind, pat, globsuffix):
486 def _regex(kind, pat, globsuffix):
461 '''Convert a (normalized) pattern of any kind into a regular expression.
487 '''Convert a (normalized) pattern of any kind into a regular expression.
462 globsuffix is appended to the regexp of globs.'''
488 globsuffix is appended to the regexp of globs.'''
463 if not pat:
489 if not pat:
464 return ''
490 return ''
465 if kind == 're':
491 if kind == 're':
466 return pat
492 return pat
467 if kind == 'path':
493 if kind == 'path':
468 return '^' + util.re.escape(pat) + '(?:/|$)'
494 return '^' + util.re.escape(pat) + '(?:/|$)'
469 if kind == 'relglob':
495 if kind == 'relglob':
470 return '(?:|.*/)' + _globre(pat) + globsuffix
496 return '(?:|.*/)' + _globre(pat) + globsuffix
471 if kind == 'relpath':
497 if kind == 'relpath':
472 return util.re.escape(pat) + '(?:/|$)'
498 return util.re.escape(pat) + '(?:/|$)'
473 if kind == 'relre':
499 if kind == 'relre':
474 if pat.startswith('^'):
500 if pat.startswith('^'):
475 return pat
501 return pat
476 return '.*' + pat
502 return '.*' + pat
477 return _globre(pat) + globsuffix
503 return _globre(pat) + globsuffix
478
504
479 def _buildmatch(ctx, kindpats, globsuffix, listsubrepos, root):
505 def _buildmatch(ctx, kindpats, globsuffix, listsubrepos, root):
480 '''Return regexp string and a matcher function for kindpats.
506 '''Return regexp string and a matcher function for kindpats.
481 globsuffix is appended to the regexp of globs.'''
507 globsuffix is appended to the regexp of globs.'''
482 matchfuncs = []
508 matchfuncs = []
483
509
510 subincludes, kindpats = _expandsubinclude(kindpats, root)
511 if subincludes:
512 def matchsubinclude(f):
513 for prefix, mf in subincludes:
514 if f.startswith(prefix) and mf(f[len(prefix):]):
515 return True
516 return False
517 matchfuncs.append(matchsubinclude)
518
484 fset, kindpats = _expandsets(kindpats, ctx, listsubrepos)
519 fset, kindpats = _expandsets(kindpats, ctx, listsubrepos)
485 if fset:
520 if fset:
486 matchfuncs.append(fset.__contains__)
521 matchfuncs.append(fset.__contains__)
487
522
488 regex = ''
523 regex = ''
489 if kindpats:
524 if kindpats:
490 regex, mf = _buildregexmatch(kindpats, globsuffix)
525 regex, mf = _buildregexmatch(kindpats, globsuffix)
491 matchfuncs.append(mf)
526 matchfuncs.append(mf)
492
527
493 if len(matchfuncs) == 1:
528 if len(matchfuncs) == 1:
494 return regex, matchfuncs[0]
529 return regex, matchfuncs[0]
495 else:
530 else:
496 return regex, lambda f: any(mf(f) for mf in matchfuncs)
531 return regex, lambda f: any(mf(f) for mf in matchfuncs)
497
532
498 def _buildregexmatch(kindpats, globsuffix):
533 def _buildregexmatch(kindpats, globsuffix):
499 """Build a match function from a list of kinds and kindpats,
534 """Build a match function from a list of kinds and kindpats,
500 return regexp string and a matcher function."""
535 return regexp string and a matcher function."""
501 try:
536 try:
502 regex = '(?:%s)' % '|'.join([_regex(k, p, globsuffix)
537 regex = '(?:%s)' % '|'.join([_regex(k, p, globsuffix)
503 for (k, p, s) in kindpats])
538 for (k, p, s) in kindpats])
504 if len(regex) > 20000:
539 if len(regex) > 20000:
505 raise OverflowError
540 raise OverflowError
506 return regex, _rematcher(regex)
541 return regex, _rematcher(regex)
507 except OverflowError:
542 except OverflowError:
508 # We're using a Python with a tiny regex engine and we
543 # We're using a Python with a tiny regex engine and we
509 # made it explode, so we'll divide the pattern list in two
544 # made it explode, so we'll divide the pattern list in two
510 # until it works
545 # until it works
511 l = len(kindpats)
546 l = len(kindpats)
512 if l < 2:
547 if l < 2:
513 raise
548 raise
514 regexa, a = _buildregexmatch(kindpats[:l//2], globsuffix)
549 regexa, a = _buildregexmatch(kindpats[:l//2], globsuffix)
515 regexb, b = _buildregexmatch(kindpats[l//2:], globsuffix)
550 regexb, b = _buildregexmatch(kindpats[l//2:], globsuffix)
516 return regex, lambda s: a(s) or b(s)
551 return regex, lambda s: a(s) or b(s)
517 except re.error:
552 except re.error:
518 for k, p, s in kindpats:
553 for k, p, s in kindpats:
519 try:
554 try:
520 _rematcher('(?:%s)' % _regex(k, p, globsuffix))
555 _rematcher('(?:%s)' % _regex(k, p, globsuffix))
521 except re.error:
556 except re.error:
522 if s:
557 if s:
523 raise util.Abort(_("%s: invalid pattern (%s): %s") %
558 raise util.Abort(_("%s: invalid pattern (%s): %s") %
524 (s, k, p))
559 (s, k, p))
525 else:
560 else:
526 raise util.Abort(_("invalid pattern (%s): %s") % (k, p))
561 raise util.Abort(_("invalid pattern (%s): %s") % (k, p))
527 raise util.Abort(_("invalid pattern"))
562 raise util.Abort(_("invalid pattern"))
528
563
529 def _roots(kindpats):
564 def _roots(kindpats):
530 '''return roots and exact explicitly listed files from patterns
565 '''return roots and exact explicitly listed files from patterns
531
566
532 >>> _roots([('glob', 'g/*', ''), ('glob', 'g', ''), ('glob', 'g*', '')])
567 >>> _roots([('glob', 'g/*', ''), ('glob', 'g', ''), ('glob', 'g*', '')])
533 ['g', 'g', '.']
568 ['g', 'g', '.']
534 >>> _roots([('relpath', 'r', ''), ('path', 'p/p', ''), ('path', '', '')])
569 >>> _roots([('relpath', 'r', ''), ('path', 'p/p', ''), ('path', '', '')])
535 ['r', 'p/p', '.']
570 ['r', 'p/p', '.']
536 >>> _roots([('relglob', 'rg*', ''), ('re', 're/', ''), ('relre', 'rr', '')])
571 >>> _roots([('relglob', 'rg*', ''), ('re', 're/', ''), ('relre', 'rr', '')])
537 ['.', '.', '.']
572 ['.', '.', '.']
538 '''
573 '''
539 r = []
574 r = []
540 for kind, pat, source in kindpats:
575 for kind, pat, source in kindpats:
541 if kind == 'glob': # find the non-glob prefix
576 if kind == 'glob': # find the non-glob prefix
542 root = []
577 root = []
543 for p in pat.split('/'):
578 for p in pat.split('/'):
544 if '[' in p or '{' in p or '*' in p or '?' in p:
579 if '[' in p or '{' in p or '*' in p or '?' in p:
545 break
580 break
546 root.append(p)
581 root.append(p)
547 r.append('/'.join(root) or '.')
582 r.append('/'.join(root) or '.')
548 elif kind in ('relpath', 'path'):
583 elif kind in ('relpath', 'path'):
549 r.append(pat or '.')
584 r.append(pat or '.')
550 else: # relglob, re, relre
585 else: # relglob, re, relre
551 r.append('.')
586 r.append('.')
552 return r
587 return r
553
588
554 def _anypats(kindpats):
589 def _anypats(kindpats):
555 for kind, pat, source in kindpats:
590 for kind, pat, source in kindpats:
556 if kind in ('glob', 're', 'relglob', 'relre', 'set'):
591 if kind in ('glob', 're', 'relglob', 'relre', 'set'):
557 return True
592 return True
558
593
559 _commentre = None
594 _commentre = None
560
595
561 def readpatternfile(filepath, warn):
596 def readpatternfile(filepath, warn):
562 '''parse a pattern file, returning a list of
597 '''parse a pattern file, returning a list of
563 patterns. These patterns should be given to compile()
598 patterns. These patterns should be given to compile()
564 to be validated and converted into a match function.
599 to be validated and converted into a match function.
565
600
566 trailing white space is dropped.
601 trailing white space is dropped.
567 the escape character is backslash.
602 the escape character is backslash.
568 comments start with #.
603 comments start with #.
569 empty lines are skipped.
604 empty lines are skipped.
570
605
571 lines can be of the following formats:
606 lines can be of the following formats:
572
607
573 syntax: regexp # defaults following lines to non-rooted regexps
608 syntax: regexp # defaults following lines to non-rooted regexps
574 syntax: glob # defaults following lines to non-rooted globs
609 syntax: glob # defaults following lines to non-rooted globs
575 re:pattern # non-rooted regular expression
610 re:pattern # non-rooted regular expression
576 glob:pattern # non-rooted glob
611 glob:pattern # non-rooted glob
577 pattern # pattern of the current default type'''
612 pattern # pattern of the current default type'''
578
613
579 syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:',
614 syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:',
580 'include': 'include'}
615 'include': 'include', 'subinclude': 'subinclude'}
581 syntax = 'relre:'
616 syntax = 'relre:'
582 patterns = []
617 patterns = []
583
618
584 fp = open(filepath)
619 fp = open(filepath)
585 for line in fp:
620 for line in fp:
586 if "#" in line:
621 if "#" in line:
587 global _commentre
622 global _commentre
588 if not _commentre:
623 if not _commentre:
589 _commentre = re.compile(r'((^|[^\\])(\\\\)*)#.*')
624 _commentre = re.compile(r'((^|[^\\])(\\\\)*)#.*')
590 # remove comments prefixed by an even number of escapes
625 # remove comments prefixed by an even number of escapes
591 line = _commentre.sub(r'\1', line)
626 line = _commentre.sub(r'\1', line)
592 # fixup properly escaped comments that survived the above
627 # fixup properly escaped comments that survived the above
593 line = line.replace("\\#", "#")
628 line = line.replace("\\#", "#")
594 line = line.rstrip()
629 line = line.rstrip()
595 if not line:
630 if not line:
596 continue
631 continue
597
632
598 if line.startswith('syntax:'):
633 if line.startswith('syntax:'):
599 s = line[7:].strip()
634 s = line[7:].strip()
600 try:
635 try:
601 syntax = syntaxes[s]
636 syntax = syntaxes[s]
602 except KeyError:
637 except KeyError:
603 if warn:
638 if warn:
604 warn(_("%s: ignoring invalid syntax '%s'\n") %
639 warn(_("%s: ignoring invalid syntax '%s'\n") %
605 (filepath, s))
640 (filepath, s))
606 continue
641 continue
607
642
608 linesyntax = syntax
643 linesyntax = syntax
609 for s, rels in syntaxes.iteritems():
644 for s, rels in syntaxes.iteritems():
610 if line.startswith(rels):
645 if line.startswith(rels):
611 linesyntax = rels
646 linesyntax = rels
612 line = line[len(rels):]
647 line = line[len(rels):]
613 break
648 break
614 elif line.startswith(s+':'):
649 elif line.startswith(s+':'):
615 linesyntax = rels
650 linesyntax = rels
616 line = line[len(s) + 1:]
651 line = line[len(s) + 1:]
617 break
652 break
618 patterns.append(linesyntax + line)
653 patterns.append(linesyntax + line)
619 fp.close()
654 fp.close()
620 return patterns
655 return patterns
@@ -1,196 +1,244 b''
1 $ hg init
1 $ hg init
2
2
3 Issue562: .hgignore requires newline at end:
3 Issue562: .hgignore requires newline at end:
4
4
5 $ touch foo
5 $ touch foo
6 $ touch bar
6 $ touch bar
7 $ touch baz
7 $ touch baz
8 $ cat > makeignore.py <<EOF
8 $ cat > makeignore.py <<EOF
9 > f = open(".hgignore", "w")
9 > f = open(".hgignore", "w")
10 > f.write("ignore\n")
10 > f.write("ignore\n")
11 > f.write("foo\n")
11 > f.write("foo\n")
12 > # No EOL here
12 > # No EOL here
13 > f.write("bar")
13 > f.write("bar")
14 > f.close()
14 > f.close()
15 > EOF
15 > EOF
16
16
17 $ python makeignore.py
17 $ python makeignore.py
18
18
19 Should display baz only:
19 Should display baz only:
20
20
21 $ hg status
21 $ hg status
22 ? baz
22 ? baz
23
23
24 $ rm foo bar baz .hgignore makeignore.py
24 $ rm foo bar baz .hgignore makeignore.py
25
25
26 $ touch a.o
26 $ touch a.o
27 $ touch a.c
27 $ touch a.c
28 $ touch syntax
28 $ touch syntax
29 $ mkdir dir
29 $ mkdir dir
30 $ touch dir/a.o
30 $ touch dir/a.o
31 $ touch dir/b.o
31 $ touch dir/b.o
32 $ touch dir/c.o
32 $ touch dir/c.o
33
33
34 $ hg add dir/a.o
34 $ hg add dir/a.o
35 $ hg commit -m 0
35 $ hg commit -m 0
36 $ hg add dir/b.o
36 $ hg add dir/b.o
37
37
38 $ hg status
38 $ hg status
39 A dir/b.o
39 A dir/b.o
40 ? a.c
40 ? a.c
41 ? a.o
41 ? a.o
42 ? dir/c.o
42 ? dir/c.o
43 ? syntax
43 ? syntax
44
44
45 $ echo "*.o" > .hgignore
45 $ echo "*.o" > .hgignore
46 $ hg status
46 $ hg status
47 abort: $TESTTMP/.hgignore: invalid pattern (relre): *.o (glob)
47 abort: $TESTTMP/.hgignore: invalid pattern (relre): *.o (glob)
48 [255]
48 [255]
49
49
50 $ echo ".*\.o" > .hgignore
50 $ echo ".*\.o" > .hgignore
51 $ hg status
51 $ hg status
52 A dir/b.o
52 A dir/b.o
53 ? .hgignore
53 ? .hgignore
54 ? a.c
54 ? a.c
55 ? syntax
55 ? syntax
56
56
57 Check it does not ignore the current directory '.':
57 Check it does not ignore the current directory '.':
58
58
59 $ echo "^\." > .hgignore
59 $ echo "^\." > .hgignore
60 $ hg status
60 $ hg status
61 A dir/b.o
61 A dir/b.o
62 ? a.c
62 ? a.c
63 ? a.o
63 ? a.o
64 ? dir/c.o
64 ? dir/c.o
65 ? syntax
65 ? syntax
66
66
67 Test that patterns from ui.ignore options are read:
67 Test that patterns from ui.ignore options are read:
68
68
69 $ echo > .hgignore
69 $ echo > .hgignore
70 $ cat >> $HGRCPATH << EOF
70 $ cat >> $HGRCPATH << EOF
71 > [ui]
71 > [ui]
72 > ignore.other = $TESTTMP/.hg/testhgignore
72 > ignore.other = $TESTTMP/.hg/testhgignore
73 > EOF
73 > EOF
74 $ echo "glob:**.o" > .hg/testhgignore
74 $ echo "glob:**.o" > .hg/testhgignore
75 $ hg status
75 $ hg status
76 A dir/b.o
76 A dir/b.o
77 ? .hgignore
77 ? .hgignore
78 ? a.c
78 ? a.c
79 ? syntax
79 ? syntax
80
80
81 empty out testhgignore
81 empty out testhgignore
82 $ echo > .hg/testhgignore
82 $ echo > .hg/testhgignore
83
83
84 Test relative ignore path (issue4473):
84 Test relative ignore path (issue4473):
85
85
86 $ cat >> $HGRCPATH << EOF
86 $ cat >> $HGRCPATH << EOF
87 > [ui]
87 > [ui]
88 > ignore.relative = .hg/testhgignorerel
88 > ignore.relative = .hg/testhgignorerel
89 > EOF
89 > EOF
90 $ echo "glob:*.o" > .hg/testhgignorerel
90 $ echo "glob:*.o" > .hg/testhgignorerel
91 $ cd dir
91 $ cd dir
92 $ hg status
92 $ hg status
93 A dir/b.o
93 A dir/b.o
94 ? .hgignore
94 ? .hgignore
95 ? a.c
95 ? a.c
96 ? syntax
96 ? syntax
97
97
98 $ cd ..
98 $ cd ..
99 $ echo > .hg/testhgignorerel
99 $ echo > .hg/testhgignorerel
100 $ echo "syntax: glob" > .hgignore
100 $ echo "syntax: glob" > .hgignore
101 $ echo "re:.*\.o" >> .hgignore
101 $ echo "re:.*\.o" >> .hgignore
102 $ hg status
102 $ hg status
103 A dir/b.o
103 A dir/b.o
104 ? .hgignore
104 ? .hgignore
105 ? a.c
105 ? a.c
106 ? syntax
106 ? syntax
107
107
108 $ echo "syntax: invalid" > .hgignore
108 $ echo "syntax: invalid" > .hgignore
109 $ hg status
109 $ hg status
110 $TESTTMP/.hgignore: ignoring invalid syntax 'invalid' (glob)
110 $TESTTMP/.hgignore: ignoring invalid syntax 'invalid' (glob)
111 A dir/b.o
111 A dir/b.o
112 ? .hgignore
112 ? .hgignore
113 ? a.c
113 ? a.c
114 ? a.o
114 ? a.o
115 ? dir/c.o
115 ? dir/c.o
116 ? syntax
116 ? syntax
117
117
118 $ echo "syntax: glob" > .hgignore
118 $ echo "syntax: glob" > .hgignore
119 $ echo "*.o" >> .hgignore
119 $ echo "*.o" >> .hgignore
120 $ hg status
120 $ hg status
121 A dir/b.o
121 A dir/b.o
122 ? .hgignore
122 ? .hgignore
123 ? a.c
123 ? a.c
124 ? syntax
124 ? syntax
125
125
126 $ echo "relglob:syntax*" > .hgignore
126 $ echo "relglob:syntax*" > .hgignore
127 $ hg status
127 $ hg status
128 A dir/b.o
128 A dir/b.o
129 ? .hgignore
129 ? .hgignore
130 ? a.c
130 ? a.c
131 ? a.o
131 ? a.o
132 ? dir/c.o
132 ? dir/c.o
133
133
134 $ echo "relglob:*" > .hgignore
134 $ echo "relglob:*" > .hgignore
135 $ hg status
135 $ hg status
136 A dir/b.o
136 A dir/b.o
137
137
138 $ cd dir
138 $ cd dir
139 $ hg status .
139 $ hg status .
140 A b.o
140 A b.o
141
141
142 $ hg debugignore
142 $ hg debugignore
143 (?:(?:|.*/)[^/]*(?:/|$))
143 (?:(?:|.*/)[^/]*(?:/|$))
144
144
145 $ cd ..
145 $ cd ..
146
146
147 Check patterns that match only the directory
147 Check patterns that match only the directory
148
148
149 $ echo "^dir\$" > .hgignore
149 $ echo "^dir\$" > .hgignore
150 $ hg status
150 $ hg status
151 A dir/b.o
151 A dir/b.o
152 ? .hgignore
152 ? .hgignore
153 ? a.c
153 ? a.c
154 ? a.o
154 ? a.o
155 ? syntax
155 ? syntax
156
156
157 Check recursive glob pattern matches no directories (dir/**/c.o matches dir/c.o)
157 Check recursive glob pattern matches no directories (dir/**/c.o matches dir/c.o)
158
158
159 $ echo "syntax: glob" > .hgignore
159 $ echo "syntax: glob" > .hgignore
160 $ echo "dir/**/c.o" >> .hgignore
160 $ echo "dir/**/c.o" >> .hgignore
161 $ touch dir/c.o
161 $ touch dir/c.o
162 $ mkdir dir/subdir
162 $ mkdir dir/subdir
163 $ touch dir/subdir/c.o
163 $ touch dir/subdir/c.o
164 $ hg status
164 $ hg status
165 A dir/b.o
165 A dir/b.o
166 ? .hgignore
166 ? .hgignore
167 ? a.c
167 ? a.c
168 ? a.o
168 ? a.o
169 ? syntax
169 ? syntax
170
170
171 Check using 'include:' in ignore file
171 Check using 'include:' in ignore file
172
172
173 $ hg purge --all --config extensions.purge=
173 $ hg purge --all --config extensions.purge=
174 $ touch foo.included
174 $ touch foo.included
175
175
176 $ echo ".*.included" > otherignore
176 $ echo ".*.included" > otherignore
177 $ hg status -I "include:otherignore"
177 $ hg status -I "include:otherignore"
178 ? foo.included
178 ? foo.included
179
179
180 $ echo "include:otherignore" >> .hgignore
180 $ echo "include:otherignore" >> .hgignore
181 $ hg status
181 $ hg status
182 A dir/b.o
182 A dir/b.o
183 ? .hgignore
183 ? .hgignore
184 ? otherignore
184 ? otherignore
185
185
186 Check recursive uses of 'include:'
186 Check recursive uses of 'include:'
187
187
188 $ echo "include:nestedignore" >> otherignore
188 $ echo "include:nestedignore" >> otherignore
189 $ echo "glob:*ignore" > nestedignore
189 $ echo "glob:*ignore" > nestedignore
190 $ hg status
190 $ hg status
191 A dir/b.o
191 A dir/b.o
192
192
193 $ cp otherignore goodignore
193 $ echo "include:badignore" >> otherignore
194 $ echo "include:badignore" >> otherignore
194 $ hg status
195 $ hg status
195 skipping unreadable pattern file 'badignore': No such file or directory
196 skipping unreadable pattern file 'badignore': No such file or directory
196 A dir/b.o
197 A dir/b.o
198
199 $ mv goodignore otherignore
200
201 Check including subincludes
202
203 $ hg revert -q --all
204 $ hg purge --all --config extensions.purge=
205 $ echo ".hgignore" > .hgignore
206 $ mkdir dir1 dir2
207 $ touch dir1/file1 dir1/file2 dir2/file1 dir2/file2
208 $ echo "subinclude:dir2/.hgignore" >> .hgignore
209 $ echo "glob:file*2" > dir2/.hgignore
210 $ hg status
211 ? dir1/file1
212 ? dir1/file2
213 ? dir2/file1
214
215 Check including subincludes with regexs
216
217 $ echo "subinclude:dir1/.hgignore" >> .hgignore
218 $ echo "regexp:f.le1" > dir1/.hgignore
219
220 $ hg status
221 ? dir1/file2
222 ? dir2/file1
223
224 Check multiple levels of sub-ignores
225
226 $ mkdir dir1/subdir
227 $ touch dir1/subdir/subfile1 dir1/subdir/subfile3 dir1/subdir/subfile4
228 $ echo "subinclude:subdir/.hgignore" >> dir1/.hgignore
229 $ echo "glob:subfil*3" >> dir1/subdir/.hgignore
230
231 $ hg status
232 ? dir1/file2
233 ? dir1/subdir/subfile4
234 ? dir2/file1
235
236 Check include subignore at the same level
237
238 $ mv dir1/subdir/.hgignore dir1/.hgignoretwo
239 $ echo "regexp:f.le1" > dir1/.hgignore
240 $ echo "subinclude:.hgignoretwo" >> dir1/.hgignore
241 $ echo "glob:file*2" > dir1/.hgignoretwo
242
243 $ hg status | grep file2
244 [1]
General Comments 0
You need to be logged in to leave comments. Login now