1223
1222
subp = pathjoin(path, subf)
1226
def _translate_ignore_rule(self, rule):
1227
"""Translate a single ignore rule to a regex.
1229
There are two types of ignore rules. Those that do not contain a / are
1230
matched against the tail of the filename (that is, they do not care
1231
what directory the file is in.) Rules which do contain a slash must
1232
match the entire path. As a special case, './' at the start of the
1233
string counts as a slash in the string but is removed before matching
1234
(e.g. ./foo.c, ./src/foo.c)
1236
:return: The translated regex.
1238
if rule[:2] in ('./', '.\\'):
1240
result = fnmatch.translate(rule[2:])
1241
elif '/' in rule or '\\' in rule:
1243
result = fnmatch.translate(rule)
1245
# default rule style.
1246
result = "(?:.*/)?(?!.*/)" + fnmatch.translate(rule)
1247
assert result[-1] == '$', "fnmatch.translate did not add the expected $"
1248
return "(" + result + ")"
1250
def _combine_ignore_rules(self, rules):
1251
"""Combine a list of ignore rules into a single regex object.
1253
Each individual rule is combined with | to form a big regex, which then
1254
has $ added to it to form something like ()|()|()$. The group index for
1255
each subregex's outermost group is placed in a dictionary mapping back
1256
to the rule. This allows quick identification of the matching rule that
1258
:return: a list of the compiled regex and the matching-group index
1259
dictionaries. We return a list because python complains if you try to
1260
combine more than 100 regexes.
1265
translated_rules = []
1267
translated_rule = self._translate_ignore_rule(rule)
1268
compiled_rule = re.compile(translated_rule)
1269
groups[next_group] = rule
1270
next_group += compiled_rule.groups
1271
translated_rules.append(translated_rule)
1272
if next_group == 99:
1273
result.append((re.compile("|".join(translated_rules)), groups))
1276
translated_rules = []
1277
if len(translated_rules):
1278
result.append((re.compile("|".join(translated_rules)), groups))
1281
1226
def ignored_files(self):
1282
1227
"""Yield list of PATH, IGNORE_PATTERN"""
1297
1242
ignore_globs = set(bzrlib.DEFAULT_IGNORE)
1298
1243
ignore_globs.update(ignores.get_runtime_ignores())
1300
1244
ignore_globs.update(ignores.get_user_ignores())
1302
1245
if self.has_filename(bzrlib.IGNORE_FILENAME):
1303
1246
f = self.get_file_byname(bzrlib.IGNORE_FILENAME)
1305
1248
ignore_globs.update(ignores.parse_ignore_file(f))
1309
1251
self._ignoreset = ignore_globs
1310
self._ignore_regex = self._combine_ignore_rules(ignore_globs)
1311
1252
return ignore_globs
1313
def _get_ignore_rules_as_regex(self):
1314
"""Return a regex of the ignore rules and a mapping dict.
1316
:return: (ignore rules compiled regex, dictionary mapping rule group
1317
indices to original rule.)
1319
if getattr(self, '_ignoreset', None) is None:
1320
self.get_ignore_list()
1321
return self._ignore_regex
1254
def _flush_ignore_list_cache(self):
1255
"""Resets the cached ignore list to force a cache rebuild."""
1256
self._ignoreset = None
1257
self._ignoreglobster = None
1323
1259
def is_ignored(self, filename):
1324
1260
r"""Check whether the filename matches an ignore pattern.
1329
1265
If the file is ignored, returns the pattern which caused it to
1330
1266
be ignored, otherwise None. So this can simply be used as a
1331
1267
boolean if desired."""
1333
# TODO: Use '**' to match directories, and other extended
1334
# globbing stuff from cvs/rsync.
1336
# XXX: fnmatch is actually not quite what we want: it's only
1337
# approximately the same as real Unix fnmatch, and doesn't
1338
# treat dotfiles correctly and allows * to match /.
1339
# Eventually it should be replaced with something more
1342
rules = self._get_ignore_rules_as_regex()
1343
for regex, mapping in rules:
1344
match = regex.match(filename)
1345
if match is not None:
1346
# one or more of the groups in mapping will have a non-None
1348
groups = match.groups()
1349
rules = [mapping[group] for group in
1350
mapping if groups[group] is not None]
1268
if getattr(self, '_ignoreglobster', None) is None:
1269
self._ignoreglobster = globbing.Globster(self.get_ignore_list())
1270
return self._ignoreglobster.match(filename)
1354
1272
def kind(self, file_id):
1355
1273
return file_kind(self.id2abspath(file_id))