diff --git a/attestation/git/git.go b/attestation/git/git.go index 7f41d5b7..1e2b5b2a 100644 --- a/attestation/git/git.go +++ b/attestation/git/git.go @@ -221,14 +221,32 @@ func (a *Attestor) Attest(ctx *attestation.AttestationContext) error { a.TreeHash = commit.TreeHash.String() + if GitExists() { + a.Status, err = GitGetStatus(ctx.WorkingDir()) + if err != nil { + return err + } + } else { + a.Status, err = GoGitGetStatus(repo) + if err != nil { + return err + } + } + + return nil +} + +func GoGitGetStatus(repo *git.Repository) (map[string]Status, error) { + var gitStatuses map[string]Status = make(map[string]Status) + worktree, err := repo.Worktree() if err != nil { - return err + return map[string]Status{}, err } status, err := worktree.Status() if err != nil { - return err + return map[string]Status{}, err } for file, status := range status { @@ -241,10 +259,10 @@ func (a *Attestor) Attest(ctx *attestation.AttestationContext) error { Staging: statusCodeString(status.Staging), } - a.Status[file] = attestStatus + gitStatuses[file] = attestStatus } - return nil + return gitStatuses, nil } func (a *Attestor) Data() *Attestor { diff --git a/attestation/git/git_bin.go b/attestation/git/git_bin.go new file mode 100644 index 00000000..4fd96b86 --- /dev/null +++ b/attestation/git/git_bin.go @@ -0,0 +1,56 @@ +package git + +import ( + "os/exec" + "strings" + + "github.com/go-git/go-git/v5" +) + +// GitExists checks if the git binary is available. +// This can be used to fall back to go-git implementation. +func GitExists() bool { + + _, err := exec.LookPath("git") + if err != nil { + return false + } else { + return true + } +} + +func GitGetStatus(workDir string) (map[string]Status, error) { + + // Execute the git status --porcelain command + cmd := exec.Command("git", "-C", workDir, "status", "--porcelain", ) + outputBytes, err := cmd.Output() + if err != nil { + return map[string]Status{}, err + } + + // Convert the output to a string and split into lines + output := string(outputBytes) + lines := strings.Split(output, "\n") + + // Iterate over the lines and parse the status + var gitStatuses map[string]Status = make(map[string]Status) + for _, line := range lines { + // Skip empty lines + if len(line) == 0 { + continue + } + + // The first two characters are the status codes + repoStatus := statusCodeString(git.StatusCode(line[0])) + worktreeStatus := statusCodeString(git.StatusCode(line[1])) + filePath := strings.TrimSpace(line[2:]) + + // Append the parsed status to the list + gitStatuses[filePath] = Status{ + Staging: repoStatus, + Worktree: worktreeStatus, + } + } + + return gitStatuses, nil +}