internal/gitutil/messages.go
56 lines · 1647 bytes
1package gitutil
2
3import (
4 "fmt"
5 "os/exec"
6 "strings"
7)
8
9const zeroSHA = "0000000000000000000000000000000000000000"
10
11// LastCommitDate returns the committer date (YYYY-MM-DD) of the ref tip,
12// or "" for empty repos.
13func LastCommitDate(dir, ref string) string {
14 out, err := exec.Command("git", "-C", dir, "log", "-1", "--format=%cs", ref).Output()
15 if err != nil {
16 return ""
17 }
18 return strings.TrimSpace(string(out))
19}
20
21// HasCommit reports whether sha names a commit object present in dir.
22func HasCommit(dir, sha string) bool {
23 return exec.Command("git", "-C", dir, "cat-file", "-e", sha+"^{commit}").Run() == nil
24}
25
26type CommitMsg struct {
27 SHA string
28 Message string
29}
30
31// RevListMessages returns sha and full message for commits reachable from
32// new but not old, newest first, capped at max. An empty or zero old (new
33// branch) lists from new alone, still capped.
34func RevListMessages(dir, old, new string, max int) ([]CommitMsg, error) {
35 args := []string{"-C", dir, "rev-list", fmt.Sprintf("-n%d", max), "--format=%B%x00", new}
36 if old != "" && old != zeroSHA {
37 args = append(args, "^"+old)
38 }
39 out, err := exec.Command("git", args...).Output()
40 if err != nil {
41 return nil, fmt.Errorf("rev-list messages: %w", err)
42 }
43 var msgs []CommitMsg
44 for _, chunk := range strings.Split(string(out), "\x00") {
45 chunk = strings.TrimLeft(chunk, "\n")
46 if chunk == "" {
47 continue
48 }
49 header, body, ok := strings.Cut(chunk, "\n")
50 if !ok || !strings.HasPrefix(header, "commit ") {
51 continue
52 }
53 msgs = append(msgs, CommitMsg{SHA: strings.TrimPrefix(header, "commit "), Message: strings.TrimSpace(body)})
54 }
55 return msgs, nil
56}