- Introduce git.Diff function that uses CombinedOutput and treats exit code 1 as success (indicating changes exist). - Update prdescribe command to use git.Diff and trim whitespace when checking for empty diffs. - Enhance prdescribe output to include base branch in the status message.
85 lines
2.4 KiB
Go
85 lines
2.4 KiB
Go
package git
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os/exec"
|
|
"strings"
|
|
|
|
"gmgauthier.com/grokkit/internal/logger"
|
|
)
|
|
|
|
func Run(args []string) (string, error) {
|
|
cmdStr := "git " + strings.Join(args, " ")
|
|
logger.Debug("executing git command", "command", cmdStr, "args", args)
|
|
|
|
// nolint:gosec // intentional subprocess for git operation
|
|
out, err := exec.Command("git", args...).Output()
|
|
if err != nil {
|
|
logger.Error("git command failed",
|
|
"command", cmdStr,
|
|
"error", err)
|
|
return "", fmt.Errorf("git command failed: %w", err)
|
|
}
|
|
|
|
outputLen := len(out)
|
|
logger.Debug("git command completed",
|
|
"command", cmdStr,
|
|
"output_length", outputLen)
|
|
|
|
return string(out), nil
|
|
}
|
|
|
|
func IsRepo() bool {
|
|
logger.Debug("checking if directory is a git repository")
|
|
// nolint:gosec // intentional subprocess for git repository check
|
|
_, err := exec.Command("git", "rev-parse", "--is-inside-work-tree").Output()
|
|
isRepo := err == nil
|
|
logger.Debug("git repository check completed", "is_repo", isRepo)
|
|
return isRepo
|
|
}
|
|
|
|
func LatestTag() (string, error) {
|
|
out, err := Run([]string{"describe", "--tags", "--abbrev=0"})
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return strings.TrimSpace(out), nil
|
|
}
|
|
|
|
// PreviousTag returns the tag immediately before the given one.
|
|
func PreviousTag(current string) (string, error) {
|
|
out, err := Run([]string{"describe", "--tags", "--abbrev=0", current + "^"})
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return strings.TrimSpace(out), nil
|
|
}
|
|
|
|
// LogSince returns formatted commit log since the given ref (exactly matches the todo spec).
|
|
func LogSince(since string) (string, error) {
|
|
args := []string{"log", "--pretty=format:%s%n%b%n---", since + "..HEAD"}
|
|
return Run(args)
|
|
}
|
|
|
|
// Diff runs `git diff` and tolerates exit code 1 (differences found).
|
|
// This is normal git behavior when there *are* changes — unlike .Output().
|
|
func Diff(args []string) (string, error) {
|
|
cmdStr := "git " + strings.Join(args, " ")
|
|
logger.Debug("executing git diff", "command", cmdStr)
|
|
|
|
// nolint:gosec // intentional git subprocess
|
|
cmd := exec.Command("git", args...)
|
|
out, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
var exitErr *exec.ExitError
|
|
if errors.As(err, &exitErr) && exitErr.ExitCode() == 1 {
|
|
// legitimate diff (changes exist)
|
|
return string(out), nil
|
|
}
|
|
logger.Error("git diff failed", "command", cmdStr, "error", err)
|
|
return "", fmt.Errorf("git diff failed: %w", err)
|
|
}
|
|
return string(out), nil
|
|
}
|