[PATCH 3 of 4] match: enable 'relinclude:' syntax

Martin von Zweigbergk martinvonz at google.com
Thu May 21 13:11:16 CDT 2015


On Wed, May 20, 2015 at 3:01 PM Durham Goode <durham at fb.com> wrote:

> # HG changeset patch
> # User Durham Goode <durham at fb.com>
> # Date 1431818705 25200
> #      Sat May 16 16:25:05 2015 -0700
> # Node ID 3bbfdda372e8e4204a5440009bfbc9e81f2a93e9
> # Parent  28ac58249dbc906622e368357daadd4814f9c71c
> match: enable 'relinclude:' 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.
>
> diff --git a/mercurial/match.py b/mercurial/match.py
> --- a/mercurial/match.py
> +++ b/mercurial/match.py
> @@ -5,7 +5,7 @@
>  # This software may be used and distributed according to the terms of the
>  # GNU General Public License version 2 or any later version.
>
> -import re
> +import os, re
>  import util, pathutil
>  from i18n import _
>
> @@ -42,6 +42,25 @@ 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 = os.path.dirname(source)
> +            path = os.path.join(sourceroot, pat)
> +            newroot = os.path.dirname(path)
> +            relmatcher = match(newroot, '', [], ['include:%s' % path])
> +            prefix = os.path.relpath(newroot, root) + '/'
> +            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 +95,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
>          """
>
> @@ -349,7 +370,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
>
> @@ -455,6 +476,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__)
> @@ -551,7 +581,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 = []
>
> diff --git a/tests/test-hgignore.t b/tests/test-hgignore.t
> --- a/tests/test-hgignore.t
> +++ b/tests/test-hgignore.t
> @@ -190,7 +190,45 @@ 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
>

Should we also have a test with "subinclude:foo" (i.e. in the current
directory)?


> +
> +  $ 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
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel at selenic.com
> http://selenic.com/mailman/listinfo/mercurial-devel
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://selenic.com/pipermail/mercurial-devel/attachments/20150521/8b25bebc/attachment.html>


More information about the Mercurial-devel mailing list