forked from
tangled.org/core
fork
Configure Feed
Select the types of activity you want to include in your feed.
this repo has no description
fork
Configure Feed
Select the types of activity you want to include in your feed.
1package git
2
3import (
4 "bufio"
5 "context"
6 "errors"
7 "fmt"
8 "io"
9 "strings"
10 "time"
11
12 "tangled.org/core/api/tangled"
13
14 "github.com/go-git/go-git/v5/plumbing"
15)
16
17type PostReceiveLine struct {
18 OldSha plumbing.Hash // old sha of reference being updated
19 NewSha plumbing.Hash // new sha of reference being updated
20 Ref string // the reference being updated
21}
22
23func ParsePostReceive(buf io.Reader) ([]PostReceiveLine, error) {
24 scanner := bufio.NewScanner(buf)
25 var lines []PostReceiveLine
26 for scanner.Scan() {
27 line := scanner.Text()
28 parts := strings.SplitN(line, " ", 3)
29 if len(parts) != 3 {
30 continue
31 }
32
33 oldSha := parts[0]
34 newSha := parts[1]
35 ref := parts[2]
36
37 lines = append(lines, PostReceiveLine{
38 OldSha: plumbing.NewHash(oldSha),
39 NewSha: plumbing.NewHash(newSha),
40 Ref: ref,
41 })
42 }
43
44 if err := scanner.Err(); err != nil {
45 return nil, err
46 }
47
48 return lines, nil
49}
50
51type RefUpdateMeta struct {
52 CommitCount CommitCount
53 IsDefaultRef bool
54 LangBreakdown LangBreakdown
55}
56
57type CommitCount struct {
58 ByEmail map[string]int
59}
60
61func (g *GitRepo) RefUpdateMeta(line PostReceiveLine) (RefUpdateMeta, error) {
62 var errs error
63
64 commitCount, err := g.newCommitCount(line)
65 errors.Join(errs, err)
66
67 isDefaultRef, err := g.isDefaultBranch(line)
68 errors.Join(errs, err)
69
70 ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
71 defer cancel()
72 breakdown, err := g.AnalyzeLanguages(ctx)
73 errors.Join(errs, err)
74
75 return RefUpdateMeta{
76 CommitCount: commitCount,
77 IsDefaultRef: isDefaultRef,
78 LangBreakdown: breakdown,
79 }, errs
80}
81
82func (g *GitRepo) newCommitCount(line PostReceiveLine) (CommitCount, error) {
83 byEmail := make(map[string]int)
84 commitCount := CommitCount{
85 ByEmail: byEmail,
86 }
87
88 if line.NewSha.IsZero() {
89 return commitCount, nil
90 }
91
92 args := []string{fmt.Sprintf("--max-count=%d", 100)}
93
94 if line.OldSha.IsZero() {
95 // git rev-list <newsha> ^other-branches --not ^this-branch
96 args = append(args, line.NewSha.String())
97
98 branches, _ := g.Branches()
99 for _, b := range branches {
100 if !strings.Contains(line.Ref, b.Name) {
101 args = append(args, fmt.Sprintf("^%s", b.Name))
102 }
103 }
104
105 args = append(args, "--not")
106 args = append(args, fmt.Sprintf("^%s", line.Ref))
107 } else {
108 // git rev-list <oldsha>..<newsha>
109 args = append(args, fmt.Sprintf("%s..%s", line.OldSha.String(), line.NewSha.String()))
110 }
111
112 output, err := g.revList(args...)
113 if err != nil {
114 return commitCount, fmt.Errorf("failed to run rev-list: %w", err)
115 }
116
117 lines := strings.Split(strings.TrimSpace(string(output)), "\n")
118 if len(lines) == 1 && lines[0] == "" {
119 return commitCount, nil
120 }
121
122 for _, item := range lines {
123 obj, err := g.r.CommitObject(plumbing.NewHash(item))
124 if err != nil {
125 continue
126 }
127 commitCount.ByEmail[obj.Author.Email] += 1
128 }
129
130 return commitCount, nil
131}
132
133func (g *GitRepo) isDefaultBranch(line PostReceiveLine) (bool, error) {
134 defaultBranch, err := g.FindMainBranch()
135 if err != nil {
136 return false, err
137 }
138
139 refName := plumbing.ReferenceName(line.Ref)
140 if refName.IsBranch() {
141 return defaultBranch == refName.Short(), nil
142 }
143
144 return false, err
145}
146
147func (m RefUpdateMeta) AsRecord() tangled.GitRefUpdate_Meta {
148 var byEmail []*tangled.GitRefUpdate_IndividualEmailCommitCount
149 for e, v := range m.CommitCount.ByEmail {
150 byEmail = append(byEmail, &tangled.GitRefUpdate_IndividualEmailCommitCount{
151 Email: e,
152 Count: int64(v),
153 })
154 }
155
156 var langs []*tangled.GitRefUpdate_IndividualLanguageSize
157 for lang, size := range m.LangBreakdown {
158 langs = append(langs, &tangled.GitRefUpdate_IndividualLanguageSize{
159 Lang: lang,
160 Size: size,
161 })
162 }
163
164 return tangled.GitRefUpdate_Meta{
165 CommitCount: &tangled.GitRefUpdate_CommitCountBreakdown{
166 ByEmail: byEmail,
167 },
168 IsDefaultRef: m.IsDefaultRef,
169 LangBreakdown: &tangled.GitRefUpdate_LangBreakdown{
170 Inputs: langs,
171 },
172 }
173}