cloudflare/pint

Public

mirrored from https://github.com/cloudflare/pintAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.76.0

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

internal/git/git.go

116lines · modecode

1package git
2
3import (
4 "bufio"
5 "bytes"
6 "context"
7 "errors"
8 "fmt"
9 "log/slog"
10 "os/exec"
11 "strconv"
12 "strings"
13)
14
15type LineBlame struct {
16 Filename string
17 Commit string
18 PrevLine int
19 Line int
20}
21
22type LineBlames []LineBlame
23
24type FileBlames map[string]LineBlames
25
26type CommandRunner func(args ...string) ([]byte, error)
27
28func RunGit(args ...string) (content []byte, err error) {
29 slog.LogAttrs(context.Background(), slog.LevelDebug, "Running git command", slog.Any("args", args))
30 cmd := exec.Command("git", args...)
31 var stdout, stderr bytes.Buffer
32 cmd.Stdout = &stdout
33 cmd.Stderr = &stderr
34 if err = cmd.Run(); err != nil {
35 if stderr.Len() > 0 {
36 return nil, errors.New(stderr.String())
37 }
38 return nil, err
39 }
40 return stdout.Bytes(), nil
41}
42
43func Blame(cmd CommandRunner, path, commit string) (lines LineBlames, err error) {
44 slog.LogAttrs(context.Background(), slog.LevelDebug, "Running git blame", slog.String("path", path))
45 output, err := cmd("blame", "--line-porcelain", commit, "--", path)
46 if err != nil {
47 return nil, err
48 }
49
50 buf := bytes.NewReader(output)
51 scanner := bufio.NewScanner(buf)
52 var line string
53 var cl LineBlame
54 for scanner.Scan() {
55 line = scanner.Text()
56
57 switch {
58 case strings.HasPrefix(line, "author"):
59 continue
60 case strings.HasPrefix(line, "committer"):
61 continue
62 case strings.HasPrefix(line, "summary"):
63 continue
64 case strings.HasPrefix(line, "filename"):
65 cl.Filename = strings.Split(line, " ")[1]
66 case strings.HasPrefix(line, "previous"):
67 continue
68 case strings.HasPrefix(line, "boundary"):
69 continue
70 case strings.HasPrefix(line, "\t"):
71 lines = append(lines, cl)
72 cl.PrevLine = 0
73 default:
74 parts := strings.Split(line, " ")
75 if len(parts) < 3 {
76 return nil, fmt.Errorf("failed to parse line number from line: %q", line)
77 }
78 cl.Commit = parts[0]
79 if cl.PrevLine, err = strconv.Atoi(parts[1]); err != nil {
80 return nil, fmt.Errorf("failed to parse line number from %q: %w", line, err)
81 }
82 if cl.Line, err = strconv.Atoi(parts[2]); err != nil {
83 return nil, fmt.Errorf("failed to parse line number from %q: %w", line, err)
84 }
85 }
86 }
87 if err = scanner.Err(); err != nil {
88 return nil, err
89 }
90
91 return lines, nil
92}
93
94func HeadCommit(cmd CommandRunner) (string, error) {
95 commit, err := cmd("rev-parse", "--verify", "HEAD")
96 if err != nil {
97 return "", err
98 }
99 return strings.Trim(string(commit), "\n"), nil
100}
101
102func CurrentBranch(cmd CommandRunner) (string, error) {
103 commit, err := cmd("rev-parse", "--abbrev-ref", "HEAD")
104 if err != nil {
105 return "", err
106 }
107 return strings.Trim(string(commit), "\n"), nil
108}
109
110func CommitMessage(cmd CommandRunner, sha string) (string, error) {
111 msg, err := cmd("show", "-s", "--format=%B", sha)
112 if err != nil {
113 return "", err
114 }
115 return string(msg), err
116}
117