package resolver import ( "cliprepo/internal/tree" "os" "path/filepath" ) type Resolver struct { root string seen map[string]struct{} } func New(root string) *Resolver { return &Resolver{ root: root, seen: make(map[string]struct{}), } } // ResolvePattern 智能解析各种 pattern 并过滤 ignore 文件 func (r *Resolver) ResolvePattern(pattern string) ([]string, error) { if pattern == "*" { return r.WalkAll() } var matches []string // 处理类似 cmd/* 或 internal/tree/* 的情况 // 如果包含 * 且不是以 ** 开头,转换为通过 filepath.Walk 匹配或直接 Glob 过滤 rawMatches, err := filepath.Glob(pattern) if err != nil { return nil, err } ig, _ := tree.Load(r.root) for _, m := range rawMatches { info, err := os.Stat(m) if err != nil { continue } rel, _ := filepath.Rel(r.root, m) // 统一检查 .gitignore if ig != nil && ig.MatchesPath(rel) { continue } if info.IsDir() { // 如果匹配到的是目录(比如 internal/* 匹配到了 internal/config),则深层遍历该目录 err := filepath.Walk(m, func(path string, fInfo os.FileInfo, walkErr error) error { if walkErr != nil || fInfo.IsDir() { return nil } fRel, _ := filepath.Rel(r.root, path) if ig != nil && ig.MatchesPath(fRel) { return nil } r.add(fRel, &matches) return nil }) if err != nil { return nil, err } } else { r.add(rel, &matches) } } return matches, nil } // WalkAll 遍历全库并过滤 func (r *Resolver) WalkAll() ([]string, error) { var result []string ig, _ := tree.Load(r.root) err := filepath.Walk(r.root, func(path string, info os.FileInfo, err error) error { if err != nil { return nil } if info.IsDir() && info.Name() == ".git" { return filepath.SkipDir } rel, _ := filepath.Rel(r.root, path) if ig != nil && ig.MatchesPath(rel) { if info.IsDir() { return filepath.SkipDir } return nil } if info.IsDir() { return nil } r.add(rel, &result) return nil }) return result, err } func (r *Resolver) AddFile(path string, result *[]string) { r.add(path, result) } func (r *Resolver) add(path string, result *[]string) { if _, ok := r.seen[path]; ok { return } r.seen[path] = struct{}{} *result = append(*result, path) } func (r *Resolver) Unique(in []string) []string { seen := make(map[string]struct{}) out := make([]string, 0, len(in)) for _, f := range in { if _, ok := seen[f]; ok { continue } seen[f] = struct{}{} out = append(out, f) } return out }