forked from
tangled.org/core
fork
Configure Feed
Select the types of activity you want to include in your feed.
Monorepo for Tangled
fork
Configure Feed
Select the types of activity you want to include in your feed.
1package git
2
3import (
4 "bufio"
5 "fmt"
6 "io"
7 "strings"
8
9 "tangled.sh/tangled.sh/core/api/tangled"
10
11 "github.com/go-git/go-git/v5/plumbing"
12)
13
14type PostReceiveLine struct {
15 OldSha plumbing.Hash // old sha of reference being updated
16 NewSha plumbing.Hash // new sha of reference being updated
17 Ref string // the reference being updated
18}
19
20func ParsePostReceive(buf io.Reader) ([]PostReceiveLine, error) {
21 scanner := bufio.NewScanner(buf)
22 var lines []PostReceiveLine
23 for scanner.Scan() {
24 line := scanner.Text()
25 parts := strings.SplitN(line, " ", 3)
26 if len(parts) != 3 {
27 continue
28 }
29
30 oldSha := parts[0]
31 newSha := parts[1]
32 ref := parts[2]
33
34 lines = append(lines, PostReceiveLine{
35 OldSha: plumbing.NewHash(oldSha),
36 NewSha: plumbing.NewHash(newSha),
37 Ref: ref,
38 })
39 }
40
41 if err := scanner.Err(); err != nil {
42 return nil, err
43 }
44
45 return lines, nil
46}
47
48type RefUpdateMeta struct {
49 CommitCount CommitCount
50 IsDefaultRef bool
51}
52
53type CommitCount struct {
54 ByEmail map[string]int
55}
56
57func (g *GitRepo) RefUpdateMeta(line PostReceiveLine) RefUpdateMeta {
58 commitCount, err := g.newCommitCount(line)
59 if err != nil {
60 // TODO: non-fatal, log this
61 }
62
63 isDefaultRef, err := g.isDefaultBranch(line)
64 if err != nil {
65 // TODO: non-fatal, log this
66 }
67
68 return RefUpdateMeta{
69 CommitCount: commitCount,
70 IsDefaultRef: isDefaultRef,
71 }
72}
73
74func (g *GitRepo) newCommitCount(line PostReceiveLine) (CommitCount, error) {
75 byEmail := make(map[string]int)
76 commitCount := CommitCount{
77 ByEmail: byEmail,
78 }
79
80 if !line.NewSha.IsZero() {
81 output, err := g.revList(
82 fmt.Sprintf("--max-count=%d", 100),
83 fmt.Sprintf("%s..%s", line.OldSha.String(), line.NewSha.String()),
84 )
85 if err != nil {
86 return commitCount, fmt.Errorf("failed to run rev-list: %w", err)
87 }
88
89 lines := strings.Split(strings.TrimSpace(string(output)), "\n")
90 if len(lines) == 1 && lines[0] == "" {
91 return commitCount, nil
92 }
93
94 for _, item := range lines {
95 obj, err := g.r.CommitObject(plumbing.NewHash(item))
96 if err != nil {
97 continue
98 }
99 commitCount.ByEmail[obj.Author.Email] += 1
100 }
101 }
102
103 return commitCount, nil
104}
105
106func (g *GitRepo) isDefaultBranch(line PostReceiveLine) (bool, error) {
107 defaultBranch, err := g.FindMainBranch()
108 if err != nil {
109 return false, err
110 }
111
112 refName := plumbing.ReferenceName(line.Ref)
113 if refName.IsBranch() {
114 return defaultBranch == refName.Short(), nil
115 }
116
117 return false, err
118}
119
120func (m RefUpdateMeta) AsRecord() tangled.GitRefUpdate_Meta {
121 var byEmail []*tangled.GitRefUpdate_Meta_CommitCount_ByEmail_Elem
122 for e, v := range m.CommitCount.ByEmail {
123 byEmail = append(byEmail, &tangled.GitRefUpdate_Meta_CommitCount_ByEmail_Elem{
124 Email: e,
125 Count: int64(v),
126 })
127 }
128
129 return tangled.GitRefUpdate_Meta{
130 CommitCount: &tangled.GitRefUpdate_Meta_CommitCount{
131 ByEmail: byEmail,
132 },
133 IsDefaultRef: m.IsDefaultRef,
134 }
135}