NAME
App::FileSync::Ignore - .gitignore-style path exclusion
SYNOPSIS
my $ignore = App::FileSync::Ignore->new(patterns => [ '*.tmp', '!keep.tmp', 'build/' ]);
print "skip\n" if $ignore->match('src/a.tmp');
DESCRIPTION
Patterns follow .gitignore: * matches within one component, **/ matches any number of directories, ? one character, a leading ! re-includes, a leading / anchors at the root, and a pattern without a slash matches at any depth. Later patterns win. A path inside an ignored directory is ignored.
METHODS
new
Purpose: compile a list of patterns.
Args: patterns (arrayref of strings; undefined entries, blanks and comments are skipped), case_insensitive (boolean).
Returns: an App::FileSync::Ignore object.
Side Effects: none. The caller's list is not modified.
Usage:
my $ignore = App::FileSync::Ignore->new(patterns => $config->{ignore});
EXAMPLE
my $ignore = App::FileSync::Ignore->new(
patterns => [ '*.bak', '# editor files', '*~' ],
case_insensitive => 1,
);
API SPECIFICATION
INPUT
{
patterns => { type => 'arrayref', optional => 1 },
case_insensitive => { type => 'boolean', optional => 1 },
}
OUTPUT
{ type => 'object', isa => 'App::FileSync::Ignore' }
MESSAGES
Key Meaning Resolution
args.invalid patterns is not a list pass an arrayref
FORMAL SPECIFICATION
new(ps, ci) = ⟨compiled ↦ ⟨(neg(p), re(p, ci)) | p ← ps, ¬ comment(p)⟩⟩
ps' = ps
match
Purpose: decide whether a path is excluded.
Args: $path, relative and /-separated.
Returns: 1 when the path, or any directory above it, is ignored; otherwise 0.
Side Effects: none.
Usage:
next if $ignore->match($relative_path);
EXAMPLE
my $ignore = App::FileSync::Ignore->new(patterns => [ 'build' ]);
print $ignore->match('build/out/a.o'), "\n"; # 1
API SPECIFICATION
INPUT
[
{ name => 'self', type => 'object' },
{ name => 'path', type => 'string', optional => 1 },
]
OUTPUT
{ type => 'integer', min => 0, max => 1 }
MESSAGES
None. Called once per path, so arguments are checked by hand.
FORMAL SPECIFICATION
ignored(c) ⇔ last ⟨ ¬neg(p) | p ← compiled, c ∈ re(p) ⟩ = true
match(path) ⇔ ∃ a : {path} ∪ ancestors(path) • ignored(a)
PSEUDOCODE
candidates := path, then each directory above it
for each candidate:
state := not ignored
for each pattern in order: if it matches, state := not negated
if state is ignored: return 1
return 0
LIMITATIONS
Character classes (
[abc]) and backslash escapes are taken literally. A trailing/is dropped, sobuild/matchesbuildat any depth; asmatchhas no type information it matches a file of that name too.Matching is per path and linear in the number of patterns. Combining them into one alternation would be faster but would lose "later patterns win".
AUTHOR
Nigel Horne, <njh at nigelhorne.com>
LICENCE AND COPYRIGHT
Copyright 2026 Nigel Horne.
Usage is subject to the GPL2 licence terms. If you use it, please let me know.