package impact import ( "bytes" "fmt" "os/exec" "strings" ) // CurrentBranch returns the branch checked out in repo. func CurrentBranch(repo string) (string, error) { out, err := git(repo, "rev-parse", "--abbrev-ref", "HEAD") if err != nil { return "", err } branch := strings.TrimSpace(out) if branch == "" || branch == "HEAD" { return "", fmt.Errorf("%s is not on a branch; pass --branch", repo) } return branch, nil } // ChangedFiles lists the paths that differ between base and branch, using the // three-dot form so only the branch's own commits count. Renames contribute // both the old and the new path, because a moved manifest changes the classes // at both ends. func ChangedFiles(repo, base, branch string) ([]string, error) { out, err := git(repo, "diff", "--name-status", "--find-renames", base+"..."+branch) if err != nil { return nil, err } var files []string for _, line := range strings.Split(out, "\n") { fields := strings.Split(strings.TrimSpace(line), "\t") if len(fields) < 2 || fields[0] == "" { continue } files = append(files, fields[1:]...) } return uniqueSorted(files), nil } func git(repo string, args ...string) (string, error) { cmd := exec.Command("git", append([]string{"-C", repo}, args...)...) var stdout, stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr if err := cmd.Run(); err != nil { return "", fmt.Errorf("git %s: %w: %s", strings.Join(args, " "), err, strings.TrimSpace(stderr.String())) } return stdout.String(), nil }