|
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 ignore(root, files, warn): |
|
56 '''return matcher covering patterns in 'files'. |
|
57 |
|
58 the files parsed for patterns include: |
|
59 .hgignore in the repository root |
|
60 any additional files specified in the [ui] section of ~/.hgrc |
|
61 |
|
62 trailing white space is dropped. |
|
63 the escape character is backslash. |
|
64 comments start with #. |
|
65 empty lines are skipped. |
|
66 |
|
67 lines can be of the following formats: |
|
68 |
|
69 syntax: regexp # defaults following lines to non-rooted regexps |
|
70 syntax: glob # defaults following lines to non-rooted globs |
|
71 re:pattern # non-rooted regular expression |
|
72 glob:pattern # non-rooted glob |
|
73 pattern # pattern of the current default type''' |
|
74 |
|
75 pats = {} |
|
76 for f in files: |
|
77 try: |
|
78 pats[f] = [] |
|
79 fp = open(f) |
|
80 pats[f], warnings = ignorepats(fp) |
|
81 for warning in warnings: |
|
82 warn("%s: %s\n" % (f, warning)) |
|
83 except IOError, inst: |
|
84 if f != files[0]: |
|
85 warn(_("skipping unreadable ignore file '%s': %s\n") % |
|
86 (f, inst.strerror)) |
|
87 |
|
88 allpats = [] |
|
89 [allpats.extend(patlist) for patlist in pats.values()] |
|
90 if not allpats: |
|
91 return util.never |
|
92 |
|
93 try: |
|
94 ignorefunc = match.match(root, '', [], allpats) |
|
95 except util.Abort: |
|
96 # Re-raise an exception where the src is the right file |
|
97 for f, patlist in pats.iteritems(): |
|
98 try: |
|
99 match.match(root, '', [], patlist) |
|
100 except util.Abort, inst: |
|
101 raise util.Abort('%s: %s' % (f, inst[0])) |
|
102 |
|
103 return ignorefunc |