comparison mercurial/match.py @ 25283:19d0e5efa6ca

match: enable 'subinclude:' syntax This adds a new rule syntax that allows the user to include a pattern file, but only have those patterns match against files underneath the subdirectory of the pattern file. This is useful when you have nested projects in a repository and the inner projects wants to set up ignore rules that won't affect other projects in the repository. It is also useful in high commit rate repositories for removing the root .hgignore as a point of contention.
author Durham Goode <durham@fb.com>
date Sat, 16 May 2015 16:25:05 -0700
parents f9a29dc964a3
children caaf4045eca8
comparison
equal deleted inserted replaced
25282:0f28815ef066 25283:19d0e5efa6ca
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:
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
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.
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 = ''
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: