internal/gitutil/messages.go

8246c58f81115a972853af8d0ed7cbaa35bd1339
gitbay/internal/gitutil/messages.go history · blame · raw

94 lines · 2832 bytes

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