package git import ( "bytes" "context" "errors" "fmt" "os/exec" "strings" "time" ) func Run(dir string, args ...string) (string, error) { out, err := RunBytes(dir, args...) if err != nil { return "", err } return strings.TrimSpace(string(out)), nil } func RunBytes(dir string, args ...string) ([]byte, error) { ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) defer cancel() cmd := exec.CommandContext(ctx, "git", args...) cmd.Dir = dir var stdout, stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr if err := cmd.Run(); err != nil { if stderr.Len() > 0 { return nil, fmt.Errorf("git %s: %w: %s", strings.Join(args, " "), err, strings.TrimSpace(stderr.String())) } return nil, fmt.Errorf("git %s: %w", strings.Join(args, " "), err) } return stdout.Bytes(), nil } func RunExitCodeOneOK(dir string, args ...string) ([]byte, error) { ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) defer cancel() cmd := exec.CommandContext(ctx, "git", args...) cmd.Dir = dir var stdout, stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr err := cmd.Run() if err == nil { return stdout.Bytes(), nil } var exitErr *exec.ExitError if errors.As(err, &exitErr) && exitErr.ExitCode() == 1 { return stdout.Bytes(), nil } if stderr.Len() > 0 { return nil, fmt.Errorf("git %s: %w: %s", strings.Join(args, " "), err, strings.TrimSpace(stderr.String())) } return nil, fmt.Errorf("git %s: %w", strings.Join(args, " "), err) }