135
|
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, incorporated herein by reference.
|
|
7
|
|
8 from i18n import _
|
|
9 import util, match
|
|
10 import re
|
|
11
|
|
12 _commentre = None
|
|
13
|
|
14 def _parselines(fp):
|
|
15 for line in fp:
|
|
16 if "#" in line:
|
|
17 global _commentre
|
|
18 if not _commentre:
|
|
19 _commentre = re.compile(r'((^|[^\\])(\\\\)*)#.*')
|
|
20 # remove comments prefixed by an even number of escapes
|
|
21 line = _commentre.sub(r'\1', line)
|
|
22 # fixup properly escaped comments that survived the above
|
|
23 line = line.replace("\\#", "#")
|
|
24 line = line.rstrip()
|
|
25 if line:
|
|
26 yield line
|
|
27
|
|
28 def ignore(root, files, warn):
|
|
29 '''return the contents of .hgignore files as a list of patterns.
|
|
30
|
|
31 the files parsed for patterns include:
|
|
32 .hgignore in the repository root
|
|
33 any additional files specified in the [ui] section of ~/.hgrc
|
|
34
|
|
35 trailing white space is dropped.
|
|
36 the escape character is backslash.
|
|
37 comments start with #.
|
|
38 empty lines are skipped.
|
|
39
|
|
40 lines can be of the following formats:
|
|
41
|
|
42 syntax: regexp # defaults following lines to non-rooted regexps
|
|
43 syntax: glob # defaults following lines to non-rooted globs
|
|
44 re:pattern # non-rooted regular expression
|
|
45 glob:pattern # non-rooted glob
|
|
46 pattern # pattern of the current default type'''
|
|
47
|
|
48 syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:'}
|
|
49 pats = {}
|
|
50 for f in files:
|
|
51 try:
|
|
52 pats[f] = []
|
|
53 fp = open(f)
|
|
54 syntax = 'relre:'
|
|
55 for line in _parselines(fp):
|
|
56 if line.startswith('syntax:'):
|
|
57 s = line[7:].strip()
|
|
58 try:
|
|
59 syntax = syntaxes[s]
|
|
60 except KeyError:
|
|
61 warn(_("%s: ignoring invalid syntax '%s'\n") % (f, s))
|
|
62 continue
|
|
63 pat = syntax + line
|
|
64 for s, rels in syntaxes.iteritems():
|
|
65 if line.startswith(rels):
|
|
66 pat = line
|
|
67 break
|
|
68 elif line.startswith(s+':'):
|
|
69 pat = rels + line[len(s)+1:]
|
|
70 break
|
|
71 pats[f].append(pat)
|
|
72 except IOError, inst:
|
|
73 if f != files[0]:
|
|
74 warn(_("skipping unreadable ignore file '%s': %s\n") %
|
|
75 (f, inst.strerror))
|
|
76
|
|
77 allpats = []
|
|
78 [allpats.extend(patlist) for patlist in pats.values()]
|
|
79 if not allpats:
|
|
80 return util.never
|
|
81
|
|
82 try:
|
|
83 ignorefunc = match.match(root, '', [], allpats)
|
|
84 except util.Abort:
|
|
85 # Re-raise an exception where the src is the right file
|
|
86 for f, patlist in pats.iteritems():
|
|
87 try:
|
|
88 match.match(root, '', [], patlist)
|
|
89 except util.Abort, inst:
|
|
90 raise util.Abort('%s: %s' % (f, inst[0]))
|
|
91
|
|
92 return ignorefunc
|