internal/gitutil/messages.go

65 lines · 1889 bytes

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