Skip to content

feat: add use_gitignore option to exclude ignored files from sources/generates#2773

Open
vmaerten wants to merge 6 commits intomainfrom
git-ignore
Open

feat: add use_gitignore option to exclude ignored files from sources/generates#2773
vmaerten wants to merge 6 commits intomainfrom
git-ignore

Conversation

@vmaerten
Copy link
Copy Markdown
Member

@vmaerten vmaerten commented Apr 4, 2026

Summary

Add use_gitignore: true option at the Taskfile (global) and task level to automatically exclude files matching .gitignore rules from sources and generates glob resolution

Uses lightweight sabhiram/go-gitignore library (zero transitive dependencies) instead of go-git

@vmaerten vmaerten changed the title ✨ feat: add gitignore option to exclude ignored files from sources/generates feat: add gitignore option to exclude ignored files from sources/generates Apr 4, 2026
@vmaerten vmaerten changed the title feat: add gitignore option to exclude ignored files from sources/generates feat: add use_gitignore option to exclude ignored files from sources/generates Apr 4, 2026
@vmaerten vmaerten marked this pull request as ready for review April 9, 2026 06:09
@trulede
Copy link
Copy Markdown
Contributor

trulede commented Apr 19, 2026

sabhiram/go-gitignore is a single file, you might just copy it into the Task repo.

Perhaps a test for negation (!) in a nested .gitignore file, or similar.

…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.
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_gitignore and 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-gitignore dependency.

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.

Comment on lines +63 to +67
line := scanner.Text()
if line != "" && !strings.HasPrefix(line, "#") {
lines = append(lines, line)
}
}
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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
}

Copilot uses AI. Check for mistakes.
Comment on lines +71 to +76
// 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
}
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines +78 to +88
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
}
}
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment thread taskfile/ast/task.go
Comment on lines +79 to +80
// ShouldUseGitignore returns true if the task has gitignore filtering explicitly enabled.
// Returns false if UseGitignore is nil (not set) or explicitly set to false.
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
// 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.

Copilot uses AI. Check for mistakes.
Comment thread go.mod
Comment on lines 20 to +35
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
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants