<div dir="ltr"><br><br><div class="gmail_quote">On Wed, May 20, 2015 at 3:01 PM Durham Goode <<a href="mailto:durham@fb.com">durham@fb.com</a>> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"># HG changeset patch<br>
# User Durham Goode <<a href="mailto:durham@fb.com" target="_blank">durham@fb.com</a>><br>
# Date 1431818705 25200<br>
# Sat May 16 16:25:05 2015 -0700<br>
# Node ID 3bbfdda372e8e4204a5440009bfbc9e81f2a93e9<br>
# Parent 28ac58249dbc906622e368357daadd4814f9c71c<br>
match: enable 'relinclude:' syntax<br>
<br>
This adds a new rule syntax that allows the user to include a pattern file, but<br>
only have those patterns match against files underneath the subdirectory of the<br>
pattern file.<br>
<br>
This is useful when you have nested projects in a repository and the inner<br>
projects wants to set up ignore rules that won't affect other projects in the<br>
repository. It is also useful in high commit rate repositories for removing the<br>
root .hgignore as a point of contention.<br>
<br>
diff --git a/mercurial/match.py b/mercurial/match.py<br>
--- a/mercurial/match.py<br>
+++ b/mercurial/match.py<br>
@@ -5,7 +5,7 @@<br>
# This software may be used and distributed according to the terms of the<br>
# GNU General Public License version 2 or any later version.<br>
<br>
-import re<br>
+import os, re<br>
import util, pathutil<br>
from i18n import _<br>
<br>
@@ -42,6 +42,25 @@ def _expandsets(kindpats, ctx, listsubre<br>
other.append((kind, pat, source))<br>
return fset, other<br>
<br>
+def _expandsubinclude(kindpats, root):<br>
+ '''Returns the list of subinclude matchers and the kindpats without the<br>
+ subincludes in it.'''<br>
+ relmatchers = []<br>
+ other = []<br>
+<br>
+ for kind, pat, source in kindpats:<br>
+ if kind == 'subinclude':<br>
+ sourceroot = os.path.dirname(source)<br>
+ path = os.path.join(sourceroot, pat)<br>
+ newroot = os.path.dirname(path)<br>
+ relmatcher = match(newroot, '', [], ['include:%s' % path])<br>
+ prefix = os.path.relpath(newroot, root) + '/'<br>
+ relmatchers.append((prefix, relmatcher))<br>
+ else:<br>
+ other.append((kind, pat, source))<br>
+<br>
+ return relmatchers, other<br>
+<br>
def _kindpatsalwaysmatch(kindpats):<br>
""""Checks whether the kindspats match everything, as e.g.<br>
'relpath:.' does.<br>
@@ -76,6 +95,8 @@ class match(object):<br>
'relre:<regexp>' - a regexp that needn't match the start of a name<br>
'set:<fileset>' - a fileset expression<br>
'include:<path>' - a file of patterns to read and include<br>
+ 'subinclude:<path>' - a file of patterns to match against files under<br>
+ the same directory<br>
'<something>' - a pattern of the specified default type<br>
"""<br>
<br>
@@ -349,7 +370,7 @@ def _patsplit(pattern, default):<br>
if ':' in pattern:<br>
kind, pat = pattern.split(':', 1)<br>
if kind in ('re', 'glob', 'path', 'relglob', 'relpath', 'relre',<br>
- 'listfile', 'listfile0', 'set', 'include'):<br>
+ 'listfile', 'listfile0', 'set', 'include', 'subinclude'):<br>
return kind, pat<br>
return default, pattern<br>
<br>
@@ -455,6 +476,15 @@ def _buildmatch(ctx, kindpats, globsuffi<br>
globsuffix is appended to the regexp of globs.'''<br>
matchfuncs = []<br>
<br>
+ subincludes, kindpats = _expandsubinclude(kindpats, root)<br>
+ if subincludes:<br>
+ def matchsubinclude(f):<br>
+ for prefix, mf in subincludes:<br>
+ if f.startswith(prefix) and mf(f[len(prefix):]):<br>
+ return True<br>
+ return False<br>
+ matchfuncs.append(matchsubinclude)<br>
+<br>
fset, kindpats = _expandsets(kindpats, ctx, listsubrepos)<br>
if fset:<br>
matchfuncs.append(fset.__contains__)<br>
@@ -551,7 +581,7 @@ def readpatternfile(filepath, warn):<br>
pattern # pattern of the current default type'''<br>
<br>
syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:',<br>
- 'include': 'include'}<br>
+ 'include': 'include', 'subinclude': 'subinclude'}<br>
syntax = 'relre:'<br>
patterns = []<br>
<br>
diff --git a/tests/test-hgignore.t b/tests/test-hgignore.t<br>
--- a/tests/test-hgignore.t<br>
+++ b/tests/test-hgignore.t<br>
@@ -190,7 +190,45 @@ Check recursive uses of 'include:'<br>
$ hg status<br>
A dir/b.o<br>
<br>
+ $ cp otherignore goodignore<br>
$ echo "include:badignore" >> otherignore<br>
$ hg status<br>
skipping unreadable pattern file 'badignore': No such file or directory<br>
A dir/b.o<br>
+<br>
+ $ mv goodignore otherignore<br>
+<br>
+Check including subincludes<br></blockquote><div><br></div><div>Should we also have a test with "subinclude:foo" (i.e. in the current directory)?</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+<br>
+ $ hg revert -q --all<br>
+ $ hg purge --all --config extensions.purge=<br>
+ $ echo ".hgignore" > .hgignore<br>
+ $ mkdir dir1 dir2<br>
+ $ touch dir1/file1 dir1/file2 dir2/file1 dir2/file2<br>
+ $ echo "subinclude:dir2/.hgignore" >> .hgignore<br>
+ $ echo "glob:file*2" > dir2/.hgignore<br>
+ $ hg status<br>
+ ? dir1/file1<br>
+ ? dir1/file2<br>
+ ? dir2/file1<br>
+<br>
+Check including subincludes with regexs<br>
+<br>
+ $ echo "subinclude:dir1/.hgignore" >> .hgignore<br>
+ $ echo "regexp:f.le1" > dir1/.hgignore<br>
+<br>
+ $ hg status<br>
+ ? dir1/file2<br>
+ ? dir2/file1<br>
+<br>
+Check multiple levels of sub-ignores<br>
+<br>
+ $ mkdir dir1/subdir<br>
+ $ touch dir1/subdir/subfile1 dir1/subdir/subfile3 dir1/subdir/subfile4<br>
+ $ echo "subinclude:subdir/.hgignore" >> dir1/.hgignore<br>
+ $ echo "glob:subfil*3" >> dir1/subdir/.hgignore<br>
+<br>
+ $ hg status<br>
+ ? dir1/file2<br>
+ ? dir1/subdir/subfile4<br>
+ ? dir2/file1<br>
_______________________________________________<br>
Mercurial-devel mailing list<br>
<a href="mailto:Mercurial-devel@selenic.com" target="_blank">Mercurial-devel@selenic.com</a><br>
<a href="http://selenic.com/mailman/listinfo/mercurial-devel" target="_blank">http://selenic.com/mailman/listinfo/mercurial-devel</a><br>
</blockquote></div></div>