From 69148f9edb0a4b86fc391997c6af7e957362f215 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Sat, 28 Feb 2026 16:20:05 -0700 Subject: [PATCH] feat(monorel): skip untracked directories during recursive walk Add buildTrackedDirs which runs "git ls-files" once from the walk root and builds a set of directories containing at least one tracked file. findMainPackages uses this set to skip untracked directories (dist/, vendor/, node_modules/, build artifacts, etc.) without having to enumerate git-ignored paths explicitly. Falls back to walking everything when git is unavailable or the directory is not inside a repository. --- tools/monorel/main.go | 51 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/tools/monorel/main.go b/tools/monorel/main.go index bc3c705..a132256 100644 --- a/tools/monorel/main.go +++ b/tools/monorel/main.go @@ -498,15 +498,24 @@ func expandPaths(paths []string, recursive, all bool) ([]string, error) { // any directory listed in stopMarkers (e.g. .git directories), preventing // the walk from crossing into a parent repository. // -// By default directories whose names begin with '.' or '_' are skipped (they -// are conventionally hidden or disabled). Pass all=true (the -A flag) to -// include them; in that mode ReadDir failures are downgraded to warnings so +// Only directories that contain at least one git-tracked file are visited; +// untracked directories (dist/, vendor/, node_modules/, build artifacts, etc.) +// are skipped automatically. +// +// By default directories whose names begin with '.' or '_' are also skipped +// (they are conventionally hidden or disabled). Pass all=true (the -A flag) +// to include them; in that mode ReadDir failures are downgraded to warnings so // that a single unreadable directory doesn't abort the whole walk. func findMainPackages(root string, all bool) ([]string, error) { abs, err := filepath.Abs(root) if err != nil { return nil, fmt.Errorf("resolving %s: %w", root, err) } + // Build the set of directories that contain at least one tracked file. + // Nil means git is unavailable; in that case we fall back to walking + // everything (pre-existing behaviour). + trackedDirs := buildTrackedDirs(abs) + var paths []string var walk func(dir string) error walk = func(dir string) error { @@ -544,7 +553,12 @@ func findMainPackages(root string, all bool) ([]string, error) { if !all && len(name) > 0 && (name[0] == '.' || name[0] == '_') { continue } - if err := walk(filepath.Join(dir, name)); err != nil { + child := filepath.Join(dir, name) + // Skip directories that contain no git-tracked files. + if trackedDirs != nil && !trackedDirs[child] { + continue + } + if err := walk(child); err != nil { return err } } @@ -553,6 +567,35 @@ func findMainPackages(root string, all bool) ([]string, error) { return paths, walk(abs) } +// buildTrackedDirs runs "git ls-files" from dir and returns the set of all +// directories (absolute paths) that contain at least one tracked file. +// Returns nil if dir is not inside a git repository or git is unavailable, +// in which case the caller proceeds without git-tracking filtering. +func buildTrackedDirs(dir string) map[string]bool { + cmd := exec.Command("git", "ls-files") + cmd.Dir = dir + out, err := cmd.Output() + if err != nil { + return nil + } + dirs := make(map[string]bool) + for _, line := range strings.Split(string(out), "\n") { + line = strings.TrimSpace(line) + if line == "" { + continue + } + abs := filepath.Join(dir, filepath.FromSlash(line)) + // Mark every ancestor directory up to (but not including) dir. + for d := filepath.Dir(abs); d != dir; d = filepath.Dir(d) { + if dirs[d] { + break // already added this path and all its ancestors + } + dirs[d] = true + } + } + return dirs +} + // findModuleRoot walks upward from absDir looking for a directory that // contains go.mod. It stops (with an error) if it encounters a stopMarker // (default: ".git") before finding go.mod, preventing searches from crossing