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.
This commit is contained in:
Durham Goode 2015-05-16 16:25:05 -07:00
parent 586027b77d
commit 9ae1033b0d
2 changed files with 85 additions and 2 deletions

View File

@ -42,6 +42,30 @@ def _expandsets(kindpats, ctx, listsubrepos):
other.append((kind, pat, source))
return fset, other
def _expandsubinclude(kindpats, root):
'''Returns the list of subinclude matchers and the kindpats without the
subincludes in it.'''
relmatchers = []
other = []
for kind, pat, source in kindpats:
if kind == 'subinclude':
sourceroot = pathutil.dirname(source)
pat = util.pconvert(pat)
path = pathutil.join(sourceroot, pat)
newroot = pathutil.dirname(path)
relmatcher = match(newroot, '', [], ['include:%s' % path])
prefix = pathutil.canonpath(root, root, newroot)
if prefix:
prefix += '/'
relmatchers.append((prefix, relmatcher))
else:
other.append((kind, pat, source))
return relmatchers, other
def _kindpatsalwaysmatch(kindpats):
""""Checks whether the kindspats match everything, as e.g.
'relpath:.' does.
@ -76,6 +100,8 @@ class match(object):
'relre:<regexp>' - a regexp that needn't match the start of a name
'set:<fileset>' - a fileset expression
'include:<path>' - a file of patterns to read and include
'subinclude:<path>' - a file of patterns to match against files under
the same directory
'<something>' - a pattern of the specified default type
"""
@ -375,7 +401,7 @@ def _patsplit(pattern, default):
if ':' in pattern:
kind, pat = pattern.split(':', 1)
if kind in ('re', 'glob', 'path', 'relglob', 'relpath', 'relre',
'listfile', 'listfile0', 'set', 'include'):
'listfile', 'listfile0', 'set', 'include', 'subinclude'):
return kind, pat
return default, pattern
@ -481,6 +507,15 @@ def _buildmatch(ctx, kindpats, globsuffix, listsubrepos, root):
globsuffix is appended to the regexp of globs.'''
matchfuncs = []
subincludes, kindpats = _expandsubinclude(kindpats, root)
if subincludes:
def matchsubinclude(f):
for prefix, mf in subincludes:
if f.startswith(prefix) and mf(f[len(prefix):]):
return True
return False
matchfuncs.append(matchsubinclude)
fset, kindpats = _expandsets(kindpats, ctx, listsubrepos)
if fset:
matchfuncs.append(fset.__contains__)
@ -577,7 +612,7 @@ def readpatternfile(filepath, warn):
pattern # pattern of the current default type'''
syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:',
'include': 'include'}
'include': 'include', 'subinclude': 'subinclude'}
syntax = 'relre:'
patterns = []

View File

@ -190,7 +190,55 @@ Check recursive uses of 'include:'
$ hg status
A dir/b.o
$ cp otherignore goodignore
$ echo "include:badignore" >> otherignore
$ hg status
skipping unreadable pattern file 'badignore': No such file or directory
A dir/b.o
$ mv goodignore otherignore
Check including subincludes
$ hg revert -q --all
$ hg purge --all --config extensions.purge=
$ echo ".hgignore" > .hgignore
$ mkdir dir1 dir2
$ touch dir1/file1 dir1/file2 dir2/file1 dir2/file2
$ echo "subinclude:dir2/.hgignore" >> .hgignore
$ echo "glob:file*2" > dir2/.hgignore
$ hg status
? dir1/file1
? dir1/file2
? dir2/file1
Check including subincludes with regexs
$ echo "subinclude:dir1/.hgignore" >> .hgignore
$ echo "regexp:f.le1" > dir1/.hgignore
$ hg status
? dir1/file2
? dir2/file1
Check multiple levels of sub-ignores
$ mkdir dir1/subdir
$ touch dir1/subdir/subfile1 dir1/subdir/subfile3 dir1/subdir/subfile4
$ echo "subinclude:subdir/.hgignore" >> dir1/.hgignore
$ echo "glob:subfil*3" >> dir1/subdir/.hgignore
$ hg status
? dir1/file2
? dir1/subdir/subfile4
? dir2/file1
Check include subignore at the same level
$ mv dir1/subdir/.hgignore dir1/.hgignoretwo
$ echo "regexp:f.le1" > dir1/.hgignore
$ echo "subinclude:.hgignoretwo" >> dir1/.hgignore
$ echo "glob:file*2" > dir1/.hgignoretwo
$ hg status | grep file2
[1]