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