feat: add use_gitignore option to exclude ignored files from sources/generates#2773
feat: add use_gitignore option to exclude ignored files from sources/generates#2773
Conversation
|
sabhiram/go-gitignore is a single file, you might just copy it into the Task repo. Perhaps a test for negation (!) in a nested |
…rates When `gitignore: true` is set at the Taskfile or task level, files matching .gitignore rules are automatically excluded from sources and generates glob resolution. This prevents rebuilds triggered by changes to files that are in .gitignore (build artifacts, generated files, etc.). Uses go-git to load .gitignore patterns including nested .gitignore files, .git/info/exclude, and global gitignore configuration.
…dency go-git pulled ~30 transitive dependencies and recursively walked the entire worktree on every Globs() call. Replace with sabhiram/go-gitignore (zero dependencies) and a simple walk from task dir up to rootDir to collect .gitignore files. Pass rootDir (Taskfile ROOT_DIR) through the checker chain to bound the search scope.
Walk up from task dir to find .git instead of threading rootDir through Globs, checkers, and itemsFromFor. Gitignore rules are discarded if no .git is found, matching ripgrep's require_git behavior. This keeps the Globs signature clean and makes the future .taskignore integration straightforward (loaded at setup like .taskrc, separate boundary).
Rename the Taskfile/task option from `gitignore` to `use_gitignore` to avoid ambiguity (could be read as "ignore git" vs "use .gitignore"). Consistent with Biome's `useIgnoreFile` naming convention.
There was a problem hiding this comment.
Pull request overview
Adds support for a use_gitignore option (global Taskfile + per-task override) to exclude files matched by .gitignore from sources / generates glob expansion, integrating the behavior into fingerprinting and watch-mode.
Changes:
- Extend Taskfile/task AST + JSON schema with
use_gitignoreand resolve task-level vs global precedence during compilation. - Update fingerprint globbing + up-to-date checks (checksum/timestamp),
for: { from: sources|generates }, and watch source collection to honor gitignore filtering. - Add gitignore-focused testdata and unit/integration tests, plus add
sabhiram/go-gitignoredependency.
Reviewed changes
Copilot reviewed 15 out of 16 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| website/src/public/schema.json | Adds use_gitignore to task + global schema. |
| watch.go | Passes gitignore setting into source collection globbing. |
| variables.go | Resolves global vs task override and threads flag into for: resolution. |
| testdata/gitignore/source.txt | New fixture source file. |
| testdata/gitignore/Taskfile.yml | New Taskfile fixture exercising global + per-task override. |
| testdata/gitignore/.gitignore | New fixture .gitignore. |
| taskfile/ast/taskfile.go | Adds global UseGitignore field + YAML decoding. |
| taskfile/ast/task.go | Adds per-task UseGitignore field + helper accessor. |
| task_test.go | Adds integration test validating checksum behavior with gitignored sources. |
| internal/fingerprint/sources_timestamp.go | Uses gitignore-aware globbing for timestamp checking. |
| internal/fingerprint/sources_checksum.go | Uses gitignore-aware globbing for checksum checking. |
| internal/fingerprint/glob.go | Adds useGitignore param and filters matched paths. |
| internal/fingerprint/gitignore_test.go | New unit tests for gitignore filtering behavior. |
| internal/fingerprint/gitignore.go | New implementation to load/apply .gitignore rules. |
| go.mod | Adds sabhiram/go-gitignore and updates module versions. |
| go.sum | Updates sums for added dependency + other module churn. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| line := scanner.Text() | ||
| if line != "" && !strings.HasPrefix(line, "#") { | ||
| lines = append(lines, line) | ||
| } | ||
| } |
There was a problem hiding this comment.
readGitignoreLines doesn't normalize CRLF line endings or check scanner.Err(). On Windows or when .gitignore is committed with CRLF, patterns will include a trailing \r and won't match as expected. Consider trimming a trailing \r (or using strings.TrimRight(line, "\r")) and returning/handling scan errors.
| line := scanner.Text() | |
| if line != "" && !strings.HasPrefix(line, "#") { | |
| lines = append(lines, line) | |
| } | |
| } | |
| line := strings.TrimRight(scanner.Text(), "\r") | |
| if line != "" && !strings.HasPrefix(line, "#") { | |
| lines = append(lines, line) | |
| } | |
| } | |
| if err := scanner.Err(); err != nil { | |
| return nil | |
| } |
| // filterGitignored removes entries from the file map that match gitignore rules. | ||
| func filterGitignored(files map[string]bool, dir string) map[string]bool { | ||
| rules := loadGitignoreRules(dir) | ||
| if len(rules) == 0 { | ||
| return files | ||
| } |
There was a problem hiding this comment.
filterGitignored loads .gitignore rules only based on the task/base dir (via loadGitignoreRules(dir)), so glob matches that live in subdirectories won't respect .gitignore files inside those subdirectories (e.g. sources like ./**/*). To match Git behavior, rule discovery needs to be based on each matched file’s directory (and its parents up to the repo root), ideally with caching to avoid repeated filesystem walks.
| for path := range files { | ||
| for _, rule := range rules { | ||
| relPath, err := filepath.Rel(rule.dir, path) | ||
| if err != nil || strings.HasPrefix(relPath, "..") { | ||
| continue | ||
| } | ||
| if rule.matcher.MatchesPath(filepath.ToSlash(relPath)) { | ||
| files[path] = false | ||
| break | ||
| } | ||
| } |
There was a problem hiding this comment.
The matching logic treats a file as ignored if any .gitignore matcher returns true, but Git uses ordered rule evaluation where later (more specific) patterns can override earlier ones (including ! negation). With the current approach, a parent rule can incorrectly ignore a file even if a deeper .gitignore un-ignores it. Consider implementing “last match wins” across the full ordered set of patterns for a path (root → leaf), including support for negation.
| // ShouldUseGitignore returns true if the task has gitignore filtering explicitly enabled. | ||
| // Returns false if UseGitignore is nil (not set) or explicitly set to false. |
There was a problem hiding this comment.
The docstring for ShouldUseGitignore says it returns true only when gitignore filtering is "explicitly enabled", but compiled tasks appear to always set UseGitignore to the resolved (task or global) value. That makes the method effectively “is gitignore enabled for this task?”, not “explicitly enabled”. Update the comment (or rename the method) to reflect the resolved behavior to avoid misleading API consumers.
| // ShouldUseGitignore returns true if the task has gitignore filtering explicitly enabled. | |
| // Returns false if UseGitignore is nil (not set) or explicitly set to false. | |
| // ShouldUseGitignore returns true if gitignore filtering is enabled for the task. | |
| // Returns false if UseGitignore is nil or set to false. |
| github.com/google/uuid v1.6.0 | ||
| github.com/hashicorp/go-getter v1.8.6 | ||
| github.com/hashicorp/go-getter v1.8.5 | ||
| github.com/joho/godotenv v1.5.1 | ||
| github.com/mitchellh/hashstructure/v2 v2.0.2 | ||
| github.com/puzpuzpuz/xsync/v4 v4.4.0 | ||
| github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 | ||
| github.com/sajari/fuzzy v1.0.0 | ||
| github.com/sebdah/goldie/v2 v2.8.0 | ||
| github.com/spf13/pflag v1.0.10 | ||
| github.com/stretchr/testify v1.11.1 | ||
| github.com/zeebo/xxh3 v1.1.0 | ||
| go.yaml.in/yaml/v3 v3.0.4 | ||
| golang.org/x/sync v0.20.0 | ||
| golang.org/x/term v0.42.0 | ||
| golang.org/x/term v0.41.0 | ||
| mvdan.cc/sh/moreinterp v0.0.0-20260120230322-19def062a997 | ||
| mvdan.cc/sh/v3 v3.13.1 | ||
| mvdan.cc/sh/v3 v3.13.0 |
There was a problem hiding this comment.
This PR’s intent is to add go-gitignore, but go.mod also downgrades several unrelated direct dependencies (e.g. go-getter, x/term, mvdan.cc/sh). That increases risk of regressions and makes the change harder to review. Please re-run module resolution (e.g. go mod tidy) with the repo’s expected Go toolchain/settings and keep dependency version changes scoped to the new library unless there’s a deliberate reason to change them.
Summary
Add
use_gitignore: trueoption at the Taskfile (global) and task level to automatically exclude files matching.gitignorerules fromsourcesandgeneratesglob resolutionUses lightweight
sabhiram/go-gitignorelibrary (zero transitive dependencies) instead ofgo-git