mercurial/ignore.py
branchstable
changeset 25855 96a38d44ba09
parent 25745 501c51d60792
parent 25854 eabba9c75061
child 25856 98a852a44673
equal deleted inserted replaced
25745:501c51d60792 25855:96a38d44ba09
     1 # ignore.py - ignored file handling for mercurial
       
     2 #
       
     3 # Copyright 2007 Matt Mackall <mpm@selenic.com>
       
     4 #
       
     5 # This software may be used and distributed according to the terms of the
       
     6 # GNU General Public License version 2 or any later version.
       
     7 
       
     8 from i18n import _
       
     9 import util, match
       
    10 import re
       
    11 
       
    12 _commentre = None
       
    13 
       
    14 def ignorepats(lines):
       
    15     '''parse lines (iterable) of .hgignore text, returning a tuple of
       
    16     (patterns, parse errors). These patterns should be given to compile()
       
    17     to be validated and converted into a match function.'''
       
    18     syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:'}
       
    19     syntax = 'relre:'
       
    20     patterns = []
       
    21     warnings = []
       
    22 
       
    23     for line in lines:
       
    24         if "#" in line:
       
    25             global _commentre
       
    26             if not _commentre:
       
    27                 _commentre = re.compile(r'((^|[^\\])(\\\\)*)#.*')
       
    28             # remove comments prefixed by an even number of escapes
       
    29             line = _commentre.sub(r'\1', line)
       
    30             # fixup properly escaped comments that survived the above
       
    31             line = line.replace("\\#", "#")
       
    32         line = line.rstrip()
       
    33         if not line:
       
    34             continue
       
    35 
       
    36         if line.startswith('syntax:'):
       
    37             s = line[7:].strip()
       
    38             try:
       
    39                 syntax = syntaxes[s]
       
    40             except KeyError:
       
    41                 warnings.append(_("ignoring invalid syntax '%s'") % s)
       
    42             continue
       
    43         pat = syntax + line
       
    44         for s, rels in syntaxes.iteritems():
       
    45             if line.startswith(rels):
       
    46                 pat = line
       
    47                 break
       
    48             elif line.startswith(s+':'):
       
    49                 pat = rels + line[len(s) + 1:]
       
    50                 break
       
    51         patterns.append(pat)
       
    52 
       
    53     return patterns, warnings
       
    54 
       
    55 def readpats(root, files, warn):
       
    56     '''return a dict mapping ignore-file-name to list-of-patterns'''
       
    57 
       
    58     pats = {}
       
    59     for f in files:
       
    60         if f in pats:
       
    61             continue
       
    62         try:
       
    63             pats[f] = []
       
    64             fp = open(f)
       
    65             pats[f], warnings = ignorepats(fp)
       
    66             fp.close()
       
    67             for warning in warnings:
       
    68                 warn("%s: %s\n" % (f, warning))
       
    69         except IOError, inst:
       
    70             if f != files[0]:
       
    71                 warn(_("skipping unreadable ignore file '%s': %s\n") %
       
    72                      (f, inst.strerror))
       
    73     return [(f, pats[f]) for f in files if f in pats]
       
    74 
       
    75 def ignore(root, files, warn):
       
    76     '''return matcher covering patterns in 'files'.
       
    77 
       
    78     the files parsed for patterns include:
       
    79     .hgignore in the repository root
       
    80     any additional files specified in the [ui] section of ~/.hgrc
       
    81 
       
    82     trailing white space is dropped.
       
    83     the escape character is backslash.
       
    84     comments start with #.
       
    85     empty lines are skipped.
       
    86 
       
    87     lines can be of the following formats:
       
    88 
       
    89     syntax: regexp # defaults following lines to non-rooted regexps
       
    90     syntax: glob   # defaults following lines to non-rooted globs
       
    91     re:pattern     # non-rooted regular expression
       
    92     glob:pattern   # non-rooted glob
       
    93     pattern        # pattern of the current default type'''
       
    94 
       
    95     pats = readpats(root, files, warn)
       
    96 
       
    97     allpats = []
       
    98     for f, patlist in pats:
       
    99         allpats.extend(patlist)
       
   100     if not allpats:
       
   101         return util.never
       
   102 
       
   103     try:
       
   104         ignorefunc = match.match(root, '', [], allpats)
       
   105     except util.Abort:
       
   106         # Re-raise an exception where the src is the right file
       
   107         for f, patlist in pats:
       
   108             try:
       
   109                 match.match(root, '', [], patlist)
       
   110             except util.Abort, inst:
       
   111                 raise util.Abort('%s: %s' % (f, inst[0]))
       
   112 
       
   113     return ignorefunc