- commit
- c32b448
- parent
- 5427dcb
- author
- Eric Bower
- date
- 2024-05-30 15:17:51 -0400 EDT
refactor: split patchsets and calc content sha
M
db.go
+2,
-0
1@@ -33,6 +33,7 @@ type Patch struct {
2 Body string `db:"body"`
3 BodyAppendix string `db:"body_appendix"`
4 CommitSha string `db:"commit_sha"`
5+ ContentSha string `db:"content_sha"`
6 Review bool `db:"review"`
7 RawText string `db:"raw_text"`
8 CreatedAt time.Time `db:"created_at"`
9@@ -80,6 +81,7 @@ CREATE TABLE IF NOT EXISTS patches (
10 body TEXT NOT NULL,
11 body_appendix TEXT NOT NULL,
12 commit_sha TEXT NOT NULL,
13+ content_sha TEXT NOT NULL,
14 review BOOLEAN NOT NULL DEFAULT false,
15 raw_text TEXT NOT NULL,
16 created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
M
pr.go
+150,
-69
1@@ -1,19 +1,22 @@
2 package git
3
4 import (
5- "bytes"
6+ "crypto/sha256"
7+ "encoding/hex"
8 "fmt"
9 "io"
10+ "strings"
11 "time"
12
13 "github.com/bluekeyes/go-gitdiff/gitdiff"
14+ "github.com/jmoiron/sqlx"
15 )
16
17 type GitPatchRequest interface {
18 GetRepos() ([]Repo, error)
19 GetRepoByID(repoID string) (*Repo, error)
20- SubmitPatchRequest(pubkey string, repoID string, patches io.Reader) (*PatchRequest, error)
21- SubmitPatch(pubkey string, prID int64, review bool, patch io.Reader) (*Patch, error)
22+ SubmitPatchRequest(repoID int64, pubkey string, patchset io.Reader) (*PatchRequest, error)
23+ SubmitPatchSet(prID int64, pubkey string, review bool, patchset io.Reader) error
24 GetPatchRequestByID(prID int64) (*PatchRequest, error)
25 GetPatchRequests() ([]*PatchRequest, error)
26 GetPatchRequestsByRepoID(repoID string) ([]*PatchRequest, error)
27@@ -100,52 +103,114 @@ func (cmd PrCmd) UpdatePatchRequest(prID int64, status string) error {
28 return err
29 }
30
31-func (cmd PrCmd) SubmitPatch(pubkey string, prID int64, review bool, patch io.Reader) (*Patch, error) {
32- pr := PatchRequest{}
33- err := cmd.Backend.DB.Get(&pr, "SELECT * FROM patch_requests WHERE id=?", prID)
34- if err != nil {
35- return nil, err
36+// calcContentSha calculates a shasum containing the important content
37+// changes related to a patch.
38+// We cannot rely on patch.CommitSha because it includes the commit date
39+// that will change when a user fetches and applies the patch locally.
40+func (cmd PrCmd) calcContentSha(diffFiles []*gitdiff.File, header *gitdiff.PatchHeader) string {
41+ authorName := ""
42+ authorEmail := ""
43+ if header.Author != nil {
44+ authorName = header.Author.Name
45+ authorEmail = header.Author.Email
46+ }
47+ content := fmt.Sprintf(
48+ "%s\n%s\n%s\n%s\n%s\n",
49+ header.Title,
50+ header.Body,
51+ authorName,
52+ authorEmail,
53+ header.AuthorDate,
54+ )
55+ for _, diff := range diffFiles {
56+ content += fmt.Sprintf(
57+ "%s->%s %s..%s %s-%s\n",
58+ diff.OldName, diff.NewName,
59+ diff.OldOIDPrefix, diff.NewOIDPrefix,
60+ diff.OldMode.String(), diff.NewMode.String(),
61+ )
62 }
63+ sha := sha256.Sum256([]byte(content))
64+ shaStr := hex.EncodeToString(sha[:])
65+ return shaStr
66+}
67
68- // need to read io.Reader from session twice
69- var buf bytes.Buffer
70- tee := io.TeeReader(patch, &buf)
71+func (cmd PrCmd) splitPatchSet(patchset string) []string {
72+ return strings.Split(patchset, "\n\n\n")
73+}
74
75- _, preamble, err := gitdiff.Parse(tee)
76+func (cmd PrCmd) parsePatchSet(patchset io.Reader) ([]*Patch, error) {
77+ patches := []*Patch{}
78+ buf := new(strings.Builder)
79+ _, err := io.Copy(buf, patchset)
80 if err != nil {
81 return nil, err
82 }
83- header, err := gitdiff.ParsePatchHeader(preamble)
84- if err != nil {
85- return nil, err
86+
87+ patchesRaw := cmd.splitPatchSet(buf.String())
88+ for _, patchRaw := range patchesRaw {
89+ reader := strings.NewReader(patchRaw)
90+ diffFiles, preamble, err := gitdiff.Parse(reader)
91+ if err != nil {
92+ return nil, err
93+ }
94+ header, err := gitdiff.ParsePatchHeader(preamble)
95+ if err != nil {
96+ return nil, err
97+ }
98+
99+ authorName := "Unknown"
100+ authorEmail := ""
101+ if header.Author != nil {
102+ authorName = header.Author.Name
103+ authorEmail = header.Author.Email
104+ }
105+
106+ contentSha := cmd.calcContentSha(diffFiles, header)
107+
108+ patches = append(patches, &Patch{
109+ AuthorName: authorName,
110+ AuthorEmail: authorEmail,
111+ AuthorDate: header.AuthorDate.UTC().String(),
112+ Title: header.Title,
113+ Body: header.Body,
114+ BodyAppendix: header.BodyAppendix,
115+ ContentSha: contentSha,
116+ CommitSha: header.SHA,
117+ RawText: patchRaw,
118+ })
119 }
120
121- patchID := 0
122- row := cmd.Backend.DB.QueryRow(
123- "INSERT INTO patches (pubkey, patch_request_id, author_name, author_email, author_date, title, body, body_appendix, commit_sha, review, raw_text) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING id",
124- pubkey,
125- prID,
126- header.Author.Name,
127- header.Author.Email,
128- header.AuthorDate,
129- header.Title,
130- header.Body,
131- header.BodyAppendix,
132- header.SHA,
133- review,
134- buf.String(),
135+ return patches, nil
136+}
137+
138+func (cmd PrCmd) createPatch(tx *sqlx.Tx, patch *Patch) (int64, error) {
139+ var patchID int64
140+ row := tx.QueryRow(
141+ "INSERT INTO patches (pubkey, patch_request_id, author_name, author_email, author_date, title, body, body_appendix, commit_sha, content_sha, raw_text) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
142+ patch.Pubkey,
143+ patch.PatchRequestID,
144+ patch.AuthorName,
145+ patch.AuthorEmail,
146+ patch.AuthorDate,
147+ patch.Title,
148+ patch.Body,
149+ patch.BodyAppendix,
150+ patch.ContentSha,
151+ patch.CommitSha,
152+ patch.RawText,
153 )
154- err = row.Scan(&patchID)
155+ err := row.Scan(&patchID)
156 if err != nil {
157- return nil, err
158+ return 0, err
159 }
160-
161- var patchRec Patch
162- err = cmd.Backend.DB.Get(&patchRec, "SELECT * FROM patches WHERE id=?", patchID)
163- return &patchRec, err
164+ if patchID == 0 {
165+ return 0, fmt.Errorf("could not create patch request")
166+ }
167+ return patchID, err
168 }
169
170-func (cmd PrCmd) SubmitPatchRequest(pubkey string, repoID string, patches io.Reader) (*PatchRequest, error) {
171+func (cmd PrCmd) SubmitPatchRequest(repoID int64, pubkey string, patchset io.Reader) (*PatchRequest, error) {
172 tx, err := cmd.Backend.DB.Beginx()
173 if err != nil {
174 return nil, err
175@@ -158,20 +223,16 @@ func (cmd PrCmd) SubmitPatchRequest(pubkey string, repoID string, patches io.Rea
176 }
177 }()
178
179- // need to read io.Reader from session twice
180- var buf bytes.Buffer
181- tee := io.TeeReader(patches, &buf)
182-
183- _, preamble, err := gitdiff.Parse(tee)
184+ patches, err := cmd.parsePatchSet(patchset)
185 if err != nil {
186 return nil, err
187 }
188- header, err := gitdiff.ParsePatchHeader(preamble)
189- if err != nil {
190- return nil, err
191+ prName := ""
192+ prText := ""
193+ if len(patches) > 0 {
194+ prName = patches[0].Title
195+ prText = patches[0].Body
196 }
197- prName := header.Title
198- prDesc := header.Body
199
200 var prID int64
201 row := tx.QueryRow(
202@@ -179,7 +240,7 @@ func (cmd PrCmd) SubmitPatchRequest(pubkey string, repoID string, patches io.Rea
203 pubkey,
204 repoID,
205 prName,
206- prDesc,
207+ prText,
208 "open",
209 time.Now(),
210 )
211@@ -191,28 +252,13 @@ func (cmd PrCmd) SubmitPatchRequest(pubkey string, repoID string, patches io.Rea
212 return nil, fmt.Errorf("could not create patch request")
213 }
214
215- authorName := "Unknown"
216- authorEmail := ""
217- if header.Author != nil {
218- authorName = header.Author.Name
219- authorEmail = header.Author.Email
220- }
221-
222- _, err = tx.Exec(
223- "INSERT INTO patches (pubkey, patch_request_id, author_name, author_email, author_date, title, body, body_appendix, commit_sha, raw_text) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
224- pubkey,
225- prID,
226- authorName,
227- authorEmail,
228- header.AuthorDate,
229- prName,
230- prDesc,
231- header.BodyAppendix,
232- header.SHA,
233- buf.String(),
234- )
235- if err != nil {
236- return nil, err
237+ for _, patch := range patches {
238+ patch.Pubkey = pubkey
239+ patch.PatchRequestID = prID
240+ _, err = cmd.createPatch(tx, patch)
241+ if err != nil {
242+ return nil, err
243+ }
244 }
245
246 err = tx.Commit()
247@@ -224,3 +270,38 @@ func (cmd PrCmd) SubmitPatchRequest(pubkey string, repoID string, patches io.Rea
248 err = cmd.Backend.DB.Get(&pr, "SELECT * FROM patch_requests WHERE id=?", prID)
249 return &pr, err
250 }
251+
252+func (cmd PrCmd) SubmitPatchSet(prID int64, pubkey string, review bool, patchset io.Reader) error {
253+ tx, err := cmd.Backend.DB.Beginx()
254+ if err != nil {
255+ return err
256+ }
257+
258+ defer func() {
259+ err := tx.Rollback()
260+ if err != nil {
261+ cmd.Backend.Logger.Error("rollback", "err", err)
262+ }
263+ }()
264+
265+ patches, err := cmd.parsePatchSet(patchset)
266+ if err != nil {
267+ return err
268+ }
269+
270+ for _, patch := range patches {
271+ patch.Pubkey = pubkey
272+ patch.PatchRequestID = prID
273+ _, err = cmd.createPatch(tx, patch)
274+ if err != nil {
275+ return err
276+ }
277+ }
278+
279+ err = tx.Commit()
280+ if err != nil {
281+ return err
282+ }
283+
284+ return err
285+}