978
978
subp = appendpath(path, subf)
981
def _translate_ignore_rule(self, rule):
982
"""Translate a single ignore rule to a regex.
984
There are two types of ignore rules. Those that do not contain a / are
985
matched against the tail of the filename (that is, they do not care
986
what directory the file is in.) Rules which do contain a slash must
987
match the entire path. As a special case, './' at the start of the
988
string counts as a slash in the string but is removed before matching
989
(e.g. ./foo.c, ./src/foo.c)
991
:return: The translated regex.
993
if rule[:2] in ('./', '.\\'):
995
result = fnmatch.translate(rule[2:])
996
elif '/' in rule or '\\' in rule:
998
result = fnmatch.translate(rule)
1000
# default rule style.
1001
result = "(?:.*/)?(?!.*/)" + fnmatch.translate(rule)
1002
assert result[-1] == '$', "fnmatch.translate did not add the expected $"
1003
return "(" + result + ")"
1005
def _combine_ignore_rules(self, rules):
1006
"""Combine a list of ignore rules into a single regex object.
1008
Each individual rule is combined with | to form a big regex, which then
1009
has $ added to it to form something like ()|()|()$. The group index for
1010
each subregex's outermost group is placed in a dictionary mapping back
1011
to the rule. This allows quick identification of the matching rule that
1013
:return: a list of the compiled regex and the matching-group index
1014
dictionaries. We return a list because python complains if you try to
1015
combine more than 100 regexes.
1020
translated_rules = []
1022
translated_rule = self._translate_ignore_rule(rule)
1023
compiled_rule = re.compile(translated_rule)
1024
groups[next_group] = rule
1025
next_group += compiled_rule.groups
1026
translated_rules.append(translated_rule)
1027
if next_group == 99:
1028
result.append((re.compile("|".join(translated_rules)), groups))
1031
translated_rules = []
1032
if len(translated_rules):
1033
result.append((re.compile("|".join(translated_rules)), groups))
982
1036
def ignored_files(self):
983
1037
"""Yield list of PATH, IGNORE_PATTERN"""
1000
1053
f = self.get_file_byname(bzrlib.IGNORE_FILENAME)
1001
1054
l.extend([line.rstrip("\n\r") for line in f.readlines()])
1002
1055
self._ignorelist = l
1056
self._ignore_regex = self._combine_ignore_rules(l)
1059
def _get_ignore_rules_as_regex(self):
1060
"""Return a regex of the ignore rules and a mapping dict.
1062
:return: (ignore rules compiled regex, dictionary mapping rule group
1063
indices to original rule.)
1065
if getattr(self, '_ignorelist', None) is None:
1066
self.get_ignore_list()
1067
return self._ignore_regex
1006
1069
def is_ignored(self, filename):
1007
1070
r"""Check whether the filename matches an ignore pattern.
1021
1084
# treat dotfiles correctly and allows * to match /.
1022
1085
# Eventually it should be replaced with something more
1025
basename = splitpath(filename)[-1]
1026
for pat in self.get_ignore_list():
1027
if '/' in pat or '\\' in pat:
1029
# as a special case, you can put ./ at the start of a
1030
# pattern; this is good to match in the top-level
1032
if pat[:2] in ('./', '.\\'):
1036
if fnmatch.fnmatchcase(filename, newpat):
1039
if fnmatch.fnmatchcase(basename, pat):
1088
rules = self._get_ignore_rules_as_regex()
1089
for regex, mapping in rules:
1090
match = regex.match(filename)
1091
if match is not None:
1092
# one or more of the groups in mapping will have a non-None group
1094
groups = match.groups()
1095
rules = [mapping[group] for group in
1096
mapping if groups[group] is not None]
1043
1100
def kind(self, file_id):