Skip to content

Commit 0626946

Browse files
committed
feat: Do not filter tracked files even if they match gitignore ...
1 parent ffc0aeb commit 0626946

File tree

2 files changed

+139
-0
lines changed

2 files changed

+139
-0
lines changed

pkg/utils/file_filter.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import (
1010
"strings"
1111
"time"
1212

13+
"github.com/go-git/go-git/v5"
14+
"github.com/go-git/go-git/v5/plumbing/object"
1315
"github.com/rs/zerolog"
1416
gitignore "github.com/sabhiram/go-gitignore"
1517
"golang.org/x/sync/semaphore"
@@ -112,6 +114,10 @@ func (fw *FileFilter) GetFilteredFiles(filesCh chan string, globs []string) chan
112114

113115
// create pattern matcher used to match filesToFilter to glob patterns
114116
globPatternMatcher := gitignore.CompileIgnoreLines(globs...)
117+
118+
// get git-tracked files to avoid filtering them out
119+
gitTrackedFiles := fw.getGitTrackedFiles()
120+
115121
go func() {
116122
ctx := context.Background()
117123
availableThreads := semaphore.NewWeighted(fw.max_threads)
@@ -126,6 +132,11 @@ func (fw *FileFilter) GetFilteredFiles(filesCh chan string, globs []string) chan
126132
}
127133
go func(f string) {
128134
defer availableThreads.Release(1)
135+
// files tracked in git should not be filtered, even if they match gitignore patterns
136+
if gitTrackedFiles[f] {
137+
filteredFilesCh <- f
138+
return
139+
}
129140
// filesToFilter that do not match the glob pattern are filtered
130141
if !globPatternMatcher.MatchesPath(f) {
131142
filteredFilesCh <- f
@@ -143,6 +154,61 @@ func (fw *FileFilter) GetFilteredFiles(filesCh chan string, globs []string) chan
143154
return filteredFilesCh
144155
}
145156

157+
// getGitTrackedFiles returns a map of absolute file paths that are tracked in git
158+
func (fw *FileFilter) getGitTrackedFiles() map[string]bool {
159+
trackedFiles := make(map[string]bool)
160+
161+
// open the git repository
162+
repo, err := git.PlainOpenWithOptions(fw.path, &git.PlainOpenOptions{
163+
DetectDotGit: true,
164+
})
165+
if err != nil {
166+
fw.logger.Debug().Msgf("failed to open git repository: %v", err)
167+
return trackedFiles
168+
}
169+
170+
// get the HEAD reference
171+
ref, err := repo.Head()
172+
if err != nil {
173+
fw.logger.Debug().Msgf("failed to get HEAD reference: %v", err)
174+
return trackedFiles
175+
}
176+
177+
// get the commit object
178+
commit, err := repo.CommitObject(ref.Hash())
179+
if err != nil {
180+
fw.logger.Debug().Msgf("failed to get commit object: %v", err)
181+
return trackedFiles
182+
}
183+
184+
// get the tree from the commit
185+
tree, err := commit.Tree()
186+
if err != nil {
187+
fw.logger.Debug().Msgf("failed to get tree from commit: %v", err)
188+
return trackedFiles
189+
}
190+
191+
// get the worktree to find the root path
192+
worktree, err := repo.Worktree()
193+
if err != nil {
194+
fw.logger.Debug().Msgf("failed to get worktree: %v", err)
195+
return trackedFiles
196+
}
197+
repoRoot := worktree.Filesystem.Root()
198+
199+
// iterate through all files in the tree
200+
err = tree.Files().ForEach(func(f *object.File) error {
201+
absolutePath := filepath.Join(repoRoot, f.Name)
202+
trackedFiles[absolutePath] = true
203+
return nil
204+
})
205+
if err != nil {
206+
fw.logger.Debug().Msgf("failed to iterate tree files: %v", err)
207+
}
208+
209+
return trackedFiles
210+
}
211+
146212
// buildGlobs iterates a list of ignore filesToFilter and returns a list of glob patterns that can be used to test for ignored filesToFilter
147213
func (fw *FileFilter) buildGlobs(ignoreFiles []string) ([]string, error) {
148214
var globs = make([]string, 0)

pkg/utils/file_filter_test.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ import (
77
"runtime"
88
"strings"
99
"testing"
10+
"time"
1011

12+
"github.com/go-git/go-git/v5"
13+
"github.com/go-git/go-git/v5/plumbing/object"
1114
"github.com/rs/zerolog/log"
1215
"github.com/stretchr/testify/assert"
1316
)
@@ -707,3 +710,73 @@ func TestDotSnykExclude_isExpired(t *testing.T) {
707710
})
708711
}
709712
}
713+
714+
func TestFileFilter_GitTrackedFilesNotFiltered(t *testing.T) {
715+
t.Run("git tracked files are not filtered even if they match gitignore", func(t *testing.T) {
716+
tempDir := t.TempDir()
717+
718+
// Initialize a git repository
719+
repo, err := git.PlainInit(tempDir, false)
720+
assert.NoError(t, err)
721+
722+
// Create a file that will be tracked and matches gitignore pattern
723+
trackedIgnoredFile := filepath.Join(tempDir, "ignored.log")
724+
createFileInPath(t, trackedIgnoredFile, []byte("tracked but ignored"))
725+
726+
// Create another file that will NOT be tracked and matches gitignore pattern
727+
untrackedIgnoredFile := filepath.Join(tempDir, "untracked.log")
728+
createFileInPath(t, untrackedIgnoredFile, []byte("untracked and ignored"))
729+
730+
// Create a regular file that doesn't match gitignore
731+
regularFile := filepath.Join(tempDir, "regular.txt")
732+
createFileInPath(t, regularFile, []byte("regular file"))
733+
734+
// Add and commit the tracked file BEFORE creating gitignore
735+
worktree, err := repo.Worktree()
736+
assert.NoError(t, err)
737+
738+
_, err = worktree.Add("ignored.log")
739+
assert.NoError(t, err)
740+
_, err = worktree.Add("regular.txt")
741+
assert.NoError(t, err)
742+
743+
_, err = worktree.Commit("initial commit", &git.CommitOptions{
744+
Author: &object.Signature{
745+
Name: "Test",
746+
Email: "test@test.com",
747+
When: time.Now(),
748+
},
749+
})
750+
assert.NoError(t, err)
751+
752+
// Now create .gitignore that ignores *.log files
753+
gitignorePath := filepath.Join(tempDir, ".gitignore")
754+
createFileInPath(t, gitignorePath, []byte("*.log\n"))
755+
756+
// Create file filter and get filtered files
757+
fileFilter := NewFileFilter(tempDir, &log.Logger)
758+
rules, err := fileFilter.GetRules([]string{".gitignore"})
759+
assert.NoError(t, err)
760+
761+
allFiles := fileFilter.GetAllFiles()
762+
filteredFiles := fileFilter.GetFilteredFiles(allFiles, rules)
763+
764+
// Collect filtered files
765+
var filteredFilesList []string
766+
for file := range filteredFiles {
767+
filteredFilesList = append(filteredFilesList, file)
768+
}
769+
770+
// The tracked file (ignored.log) should NOT be filtered out
771+
assert.Contains(t, filteredFilesList, trackedIgnoredFile, "git tracked file should not be filtered even if it matches gitignore")
772+
773+
// The untracked file (untracked.log) SHOULD be filtered out
774+
assert.NotContains(t, filteredFilesList, untrackedIgnoredFile, "untracked file matching gitignore should be filtered")
775+
776+
// The regular file should be present
777+
assert.Contains(t, filteredFilesList, regularFile, "regular file should not be filtered")
778+
779+
// The gitignore file should be present
780+
assert.Contains(t, filteredFilesList, gitignorePath, ".gitignore should not be filtered")
781+
})
782+
}

0 commit comments

Comments
 (0)