@@ -42,6 +42,30 @@ def _expandsets(kindpats, ctx, listsubre
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, globsuffi
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 = []
@@ -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]