Skip to content

Commit 097f105

Browse files
committed
Only respect gitignore
1 parent b5de857 commit 097f105

1 file changed

Lines changed: 70 additions & 39 deletions

File tree

cli/internal/tui/tui.go

Lines changed: 70 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
tea "github.com/charmbracelet/bubbletea"
1616
"github.com/charmbracelet/lipgloss"
1717
"github.com/go-git/go-git/v5"
18+
"github.com/go-git/go-git/v5/plumbing/format/gitignore"
1819
"github.com/mattn/go-isatty"
1920
)
2021

@@ -49,7 +50,7 @@ func getTTY() (in, out *os.File, cleanup func()) {
4950
// yapiFilePattern matches *.yapi, *.yapi.yaml or *.yapi.yml in subdirectories only
5051
var yapiFilePattern = regexp.MustCompile(`^.+/.+\.yapi(\.ya?ml)?$`)
5152

52-
// FindConfigFiles returns all git-tracked yapi config files relative to the current directory
53+
// FindConfigFiles returns all non-gitignored yapi config files relative to the current directory
5354
func FindConfigFiles() ([]string, error) {
5455
return findFiles(false)
5556
}
@@ -60,69 +61,99 @@ func findFiles(includeProjectConfig bool) ([]string, error) {
6061
return nil, fmt.Errorf("failed to get current working directory: %w", err)
6162
}
6263

63-
// Open the git repository (searches up for .git)
64-
repo, err := git.PlainOpenWithOptions(cwd, &git.PlainOpenOptions{
64+
var configFiles []string
65+
err = findFilesInRepo(cwd, cwd, includeProjectConfig, &configFiles)
66+
if err != nil {
67+
return nil, err
68+
}
69+
70+
if len(configFiles) == 0 {
71+
return nil, fmt.Errorf("no .yapi/.yapi.yaml/.yapi.yml files found in subdirectories")
72+
}
73+
74+
sort.Strings(configFiles)
75+
return configFiles, nil
76+
}
77+
78+
// findFilesInRepo walks a directory, respecting gitignore rules and handling submodules.
79+
func findFilesInRepo(dir, searchRoot string, includeProjectConfig bool, results *[]string) error {
80+
// Open the git repository from this directory
81+
repo, err := git.PlainOpenWithOptions(dir, &git.PlainOpenOptions{
6582
DetectDotGit: true,
6683
})
6784
if err != nil {
68-
return nil, fmt.Errorf("not in a git repository: %w", err)
85+
return fmt.Errorf("not in a git repository: %w", err)
6986
}
7087

71-
// Get worktree to find repo root
7288
wt, err := repo.Worktree()
7389
if err != nil {
74-
return nil, fmt.Errorf("failed to get worktree: %w", err)
90+
return fmt.Errorf("failed to get worktree: %w", err)
7591
}
7692
repoRoot := wt.Filesystem.Root()
7793

78-
// Read the git index (staged files = tracked files)
79-
idx, err := repo.Storer.Index()
94+
// Read gitignore patterns for this repo
95+
patterns, err := gitignore.ReadPatterns(wt.Filesystem, nil)
8096
if err != nil {
81-
return nil, fmt.Errorf("failed to read git index: %w", err)
97+
patterns = nil
8298
}
99+
matcher := gitignore.NewMatcher(patterns)
83100

84-
// Calculate relative path from repo root to cwd
85-
relCwd, err := filepath.Rel(repoRoot, cwd)
86-
if err != nil {
87-
return nil, fmt.Errorf("failed to calculate relative path: %w", err)
88-
}
89-
if relCwd == "." {
90-
relCwd = ""
91-
}
101+
return filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
102+
if err != nil {
103+
return nil
104+
}
92105

93-
var configFiles []string
94-
for _, entry := range idx.Entries {
95-
path := entry.Name
106+
// Get path relative to repo root for gitignore matching
107+
relToRepo, err := filepath.Rel(repoRoot, path)
108+
if err != nil {
109+
return nil
110+
}
96111

97-
// Skip if not under current directory
98-
if relCwd != "" && !strings.HasPrefix(path, relCwd+"/") {
99-
continue
112+
pathComponents := strings.Split(filepath.ToSlash(relToRepo), "/")
113+
114+
if info.IsDir() {
115+
if info.Name() == ".git" {
116+
return filepath.SkipDir
117+
}
118+
119+
// Check if this is a submodule (has its own .git)
120+
if path != dir {
121+
gitPath := filepath.Join(path, ".git")
122+
if _, err := os.Stat(gitPath); err == nil {
123+
// This is a submodule - recurse with new repo context
124+
_ = findFilesInRepo(path, searchRoot, includeProjectConfig, results)
125+
return filepath.SkipDir
126+
}
127+
}
128+
129+
// Check if directory is gitignored
130+
if matcher.Match(pathComponents, true) {
131+
return filepath.SkipDir
132+
}
133+
return nil
100134
}
101135

102-
// Get path relative to cwd
103-
var relPath string
104-
if relCwd != "" {
105-
relPath = strings.TrimPrefix(path, relCwd+"/")
106-
} else {
107-
relPath = path
136+
// Check if file is gitignored
137+
if matcher.Match(pathComponents, false) {
138+
return nil
139+
}
140+
141+
// Get path relative to search root for display
142+
relPath, err := filepath.Rel(searchRoot, path)
143+
if err != nil {
144+
return nil
108145
}
109146

110147
// Match .yapi.yml files
111148
base := filepath.Base(relPath)
112149
if yapiFilePattern.MatchString(relPath) {
113-
configFiles = append(configFiles, relPath)
150+
*results = append(*results, relPath)
114151
} else if includeProjectConfig && (base == "yapi.config.yml" || base == "yapi.config.yaml") {
115-
// Only include yapi.config.yml if explicitly requested
116-
configFiles = append(configFiles, relPath)
152+
*results = append(*results, relPath)
117153
}
118-
}
119154

120-
if len(configFiles) == 0 {
121-
return nil, fmt.Errorf("no .yapi/.yapi.yaml/.yapi.yml files found in subdirectories")
122-
}
123-
124-
sort.Strings(configFiles)
125-
return configFiles, nil
155+
return nil
156+
})
126157
}
127158

128159
// FindConfigFileSingle prompts the user to select a single config file.

0 commit comments

Comments
 (0)