diff --git a/README.md b/README.md index 8950522..bf8eea6 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ usage: codeowners ... -f, --file string CODEOWNERS file path -h, --help show this help message -o, --owner strings filter results by owner + -t, --tracked only show files tracked by git -u, --unowned only show unowned files (can be combined with -o) $ ls diff --git a/cmd/codeowners/main.go b/cmd/codeowners/main.go index 8a9e7e1..9a955b2 100644 --- a/cmd/codeowners/main.go +++ b/cmd/codeowners/main.go @@ -2,9 +2,11 @@ package main import ( "bufio" + "bytes" "fmt" "io" "os" + "os/exec" "path/filepath" "strings" @@ -17,11 +19,13 @@ func main() { ownerFilters []string showUnowned bool codeownersPath string + trackedOnly bool helpFlag bool ) flag.StringSliceVarP(&ownerFilters, "owner", "o", nil, "filter results by owner") flag.BoolVarP(&showUnowned, "unowned", "u", false, "only show unowned files (can be combined with -o)") flag.StringVarP(&codeownersPath, "file", "f", "", "CODEOWNERS file path") + flag.BoolVarP(&trackedOnly, "tracked", "t", false, "only show files tracked by git") flag.BoolVarP(&helpFlag, "help", "h", false, "show this help message") flag.Usage = func() { @@ -35,6 +39,11 @@ func main() { os.Exit(0) } + var trackedFiles map[string]bool + if trackedOnly { + trackedFiles = getTrackedFiles() + } + ruleset, err := loadCodeowners(codeownersPath) if err != nil { fmt.Fprintln(os.Stderr, err) @@ -57,7 +66,12 @@ func main() { for _, startPath := range paths { // godirwalk only accepts directories, so we need to handle files separately if !isDir(startPath) { - if err := printFileOwners(out, ruleset, startPath, ownerFilters, showUnowned); err != nil { + if err := printFileOwners( + out, + ruleset, + startPath, + ownerFilters, + showUnowned, trackedOnly, trackedFiles); err != nil { fmt.Fprintf(os.Stderr, "error: %v", err) os.Exit(1) } @@ -71,7 +85,7 @@ func main() { // Only show code owners for files, not directories if !d.IsDir() { - return printFileOwners(out, ruleset, path, ownerFilters, showUnowned) + return printFileOwners(out, ruleset, path, ownerFilters, showUnowned, trackedOnly, trackedFiles) } return nil }) @@ -83,7 +97,20 @@ func main() { } } -func printFileOwners(out io.Writer, ruleset codeowners.Ruleset, path string, ownerFilters []string, showUnowned bool) error { +func printFileOwners( + out io.Writer, + ruleset codeowners.Ruleset, + path string, ownerFilters []string, + showUnowned bool, + trackedOnly bool, + trackedFiles map[string]bool, +) error { + if trackedOnly { + if _, ok := trackedFiles[path]; !ok { + return nil + } + } + rule, err := ruleset.Match(path) if err != nil { return err @@ -134,3 +161,31 @@ func isDir(path string) bool { } return info.IsDir() } + +func getTrackedFiles() map[string]bool { + // Ensure the script is run inside a Git repository + if _, err := os.Stat(".git"); os.IsNotExist(err) { + fmt.Fprintln(os.Stderr, "error: this is not a Git repository.") + os.Exit(1) + } + + cmd := exec.Command("git", "ls-files") + var out bytes.Buffer + cmd.Stdout = &out + cmd.Stderr = os.Stderr + + if err := cmd.Run(); err != nil { + fmt.Fprintln(os.Stderr, "Error running git ls-files:", err) + os.Exit(1) + } + + var trackedFiles = make(map[string]bool) + files := strings.Split(out.String(), "\n") + for _, file := range files { + if file != "" { + trackedFiles[file] = true + } + } + + return trackedFiles +}