From 5fcc6a8ea05305f45a7907c82f3cc22d7092adf4 Mon Sep 17 00:00:00 2001 From: Andrew Nesbitt Date: Sun, 8 Nov 2026 17:51:16 +0000 Subject: [PATCH] Preserve source labels for added patterns --- README.md | 7 +++++++ gitignore.go | 6 ++++++ gitignore_test.go | 15 +++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/README.md b/README.md index f0d3aed..011d62f 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,13 @@ m.AddFromFile("/path/to/repo/src/.gitignore", "src") m.AddPatterns([]byte("*.log\nbuild/\n"), "") ``` +When pattern bytes came from another filesystem, use `AddPatternsWithSource` +to preserve the source label returned by `MatchDetail` and `Errors`: + +```go +m.AddPatternsWithSource(data, "src", "src/.gitignore") +``` + To limit the bytes read from each ignore file, pass `MaxIgnoreFileSize` to any constructor or walk function: ```go diff --git a/gitignore.go b/gitignore.go index e52b8aa..f1824cb 100644 --- a/gitignore.go +++ b/gitignore.go @@ -350,6 +350,12 @@ func (m *Matcher) AddPatterns(data []byte, dir string) { m.addPatterns(data, dir, "") } +// AddPatternsWithSource parses gitignore pattern lines from data, scopes them +// to the given relative directory, and retains source as match provenance. +func (m *Matcher) AddPatternsWithSource(data []byte, dir, source string) { + m.addPatterns(data, dir, source) +} + // AddFromFile reads a .gitignore file at the given absolute path and scopes // its patterns to the given relative directory. It uses the matcher's file-size // limit, if set, and records oversized files in Errors without applying any rules. diff --git a/gitignore_test.go b/gitignore_test.go index c54a7ea..69c7856 100644 --- a/gitignore_test.go +++ b/gitignore_test.go @@ -2548,6 +2548,21 @@ func TestAddPatterns(t *testing.T) { } } +func TestAddPatternsWithSource(t *testing.T) { + m := gitignore.New("") + m.AddPatternsWithSource([]byte("*.env\n[[:bogus:]]\n"), "services/api", "services/api/.gitignore") + + result := m.MatchDetail("services/api/local.env") + if !result.Ignored || result.Pattern != "*.env" || result.Line != 1 || + result.Source != "services/api/.gitignore" { + t.Fatalf("MatchDetail = %#v", result) + } + errors := m.Errors() + if len(errors) != 1 || errors[0].Source != "services/api/.gitignore" || errors[0].Line != 2 { + t.Fatalf("Errors = %#v", errors) + } +} + func TestNewEmptyRootSkipsFilesystem(t *testing.T) { // Create a temporary directory with a .gitignore that excludes *.exe dir := t.TempDir()