|
|
# ignore.py - ignored file handling for mercurial
|
|
|
#
|
|
|
# Copyright 2007 Matt Mackall <mpm@selenic.com>
|
|
|
#
|
|
|
# This software may be used and distributed according to the terms of the
|
|
|
# GNU General Public License version 2 or any later version.
|
|
|
|
|
|
from i18n import _
|
|
|
import util, match
|
|
|
import re
|
|
|
|
|
|
_commentre = None
|
|
|
|
|
|
def ignorepats(lines):
|
|
|
'''parse lines (iterable) of .hgignore text, returning a tuple of
|
|
|
(patterns, parse errors). These patterns should be given to compile()
|
|
|
to be validated and converted into a match function.'''
|
|
|
syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:'}
|
|
|
syntax = 'relre:'
|
|
|
patterns = []
|
|
|
warnings = []
|
|
|
|
|
|
for line in lines:
|
|
|
if "#" in line:
|
|
|
global _commentre
|
|
|
if not _commentre:
|
|
|
_commentre = re.compile(r'((^|[^\\])(\\\\)*)#.*')
|
|
|
# remove comments prefixed by an even number of escapes
|
|
|
line = _commentre.sub(r'\1', line)
|
|
|
# fixup properly escaped comments that survived the above
|
|
|
line = line.replace("\\#", "#")
|
|
|
line = line.rstrip()
|
|
|
if not line:
|
|
|
continue
|
|
|
|
|
|
if line.startswith('syntax:'):
|
|
|
s = line[7:].strip()
|
|
|
try:
|
|
|
syntax = syntaxes[s]
|
|
|
except KeyError:
|
|
|
warnings.append(_("ignoring invalid syntax '%s'") % s)
|
|
|
continue
|
|
|
|
|
|
linesyntax = syntax
|
|
|
for s, rels in syntaxes.iteritems():
|
|
|
if line.startswith(rels):
|
|
|
linesyntax = rels
|
|
|
line = line[len(rels):]
|
|
|
break
|
|
|
elif line.startswith(s+':'):
|
|
|
linesyntax = rels
|
|
|
line = line[len(s) + 1:]
|
|
|
break
|
|
|
patterns.append(linesyntax + line)
|
|
|
|
|
|
return patterns, warnings
|
|
|
|
|
|
def readignorefile(filepath, warn):
|
|
|
fp = open(filepath)
|
|
|
pats, warnings = ignorepats(fp)
|
|
|
fp.close()
|
|
|
for warning in warnings:
|
|
|
warn("%s: %s\n" % (filepath, warning))
|
|
|
return pats
|
|
|
|
|
|
def readpats(root, files, warn):
|
|
|
'''return a dict mapping ignore-file-name to list-of-patterns'''
|
|
|
|
|
|
pats = {}
|
|
|
for f in files:
|
|
|
if f in pats:
|
|
|
continue
|
|
|
try:
|
|
|
pats[f] = readignorefile(f, warn)
|
|
|
except IOError, inst:
|
|
|
warn(_("skipping unreadable ignore file '%s': %s\n") %
|
|
|
(f, inst.strerror))
|
|
|
|
|
|
return [(f, pats[f]) for f in files if f in pats]
|
|
|
|
|
|
def ignore(root, files, warn):
|
|
|
'''return matcher covering patterns in 'files'.
|
|
|
|
|
|
the files parsed for patterns include:
|
|
|
.hgignore in the repository root
|
|
|
any additional files specified in the [ui] section of ~/.hgrc
|
|
|
|
|
|
trailing white space is dropped.
|
|
|
the escape character is backslash.
|
|
|
comments start with #.
|
|
|
empty lines are skipped.
|
|
|
|
|
|
lines can be of the following formats:
|
|
|
|
|
|
syntax: regexp # defaults following lines to non-rooted regexps
|
|
|
syntax: glob # defaults following lines to non-rooted globs
|
|
|
re:pattern # non-rooted regular expression
|
|
|
glob:pattern # non-rooted glob
|
|
|
pattern # pattern of the current default type'''
|
|
|
|
|
|
pats = readpats(root, files, warn)
|
|
|
|
|
|
allpats = []
|
|
|
for f, patlist in pats:
|
|
|
allpats.extend(patlist)
|
|
|
if not allpats:
|
|
|
return util.never
|
|
|
|
|
|
try:
|
|
|
ignorefunc = match.match(root, '', [], allpats)
|
|
|
except util.Abort:
|
|
|
# Re-raise an exception where the src is the right file
|
|
|
for f, patlist in pats:
|
|
|
try:
|
|
|
match.match(root, '', [], patlist)
|
|
|
except util.Abort, inst:
|
|
|
raise util.Abort('%s: %s' % (f, inst[0]))
|
|
|
|
|
|
return ignorefunc
|
|
|
|