@@ -6,10 +6,44 @@ package git
66import (
77 "bufio"
88 "bytes"
9+ "fmt"
910 "io"
10- "strings"
1111)
1212
13+ const (
14+ commitHeaderGpgsig = "gpgsig"
15+ commitHeaderGpgsigSha256 = "gpgsig-sha256"
16+ )
17+
18+ func assignCommitFields (gitRepo * Repository , commit * Commit , headerKey string , headerValue []byte ) error {
19+ if len (headerValue ) > 0 && headerValue [len (headerValue )- 1 ] == '\n' {
20+ headerValue = headerValue [:len (headerValue )- 1 ] // remove trailing newline
21+ }
22+ switch headerKey {
23+ case "tree" :
24+ objID , err := NewIDFromString (string (headerValue ))
25+ if err != nil {
26+ return fmt .Errorf ("invalid tree ID %q: %w" , string (headerValue ), err )
27+ }
28+ commit .Tree = * NewTree (gitRepo , objID )
29+ case "parent" :
30+ objID , err := NewIDFromString (string (headerValue ))
31+ if err != nil {
32+ return fmt .Errorf ("invalid parent ID %q: %w" , string (headerValue ), err )
33+ }
34+ commit .Parents = append (commit .Parents , objID )
35+ case "author" :
36+ commit .Author .Decode (headerValue )
37+ case "committer" :
38+ commit .Committer .Decode (headerValue )
39+ case commitHeaderGpgsig , commitHeaderGpgsigSha256 :
40+ // if there are duplicate "gpgsig" and "gpgsig-sha256" headers, then the signature must have already been invalid
41+ // so we don't need to handle duplicate headers here
42+ commit .Signature = & CommitSignature {Signature : string (headerValue )}
43+ }
44+ return nil
45+ }
46+
1347// CommitFromReader will generate a Commit from a provided reader
1448// We need this to interpret commits from cat-file or cat-file --batch
1549//
@@ -21,90 +55,46 @@ func CommitFromReader(gitRepo *Repository, objectID ObjectID, reader io.Reader)
2155 Committer : & Signature {},
2256 }
2357
24- payloadSB := new (strings.Builder )
25- signatureSB := new (strings.Builder )
26- messageSB := new (strings.Builder )
27- message := false
28- pgpsig := false
29-
30- bufReader , ok := reader .(* bufio.Reader )
31- if ! ok {
32- bufReader = bufio .NewReader (reader )
33- }
34-
35- readLoop:
58+ bufReader := bufio .NewReader (reader )
59+ inHeader := true
60+ var payloadSB , messageSB bytes.Buffer
61+ var headerKey string
62+ var headerValue []byte
3663 for {
3764 line , err := bufReader .ReadBytes ('\n' )
38- if err != nil {
39- if err == io .EOF {
40- if message {
41- _ , _ = messageSB .Write (line )
42- }
43- _ , _ = payloadSB .Write (line )
44- break readLoop
45- }
46- return nil , err
65+ if err != nil && err != io .EOF {
66+ return nil , fmt .Errorf ("unable to read commit %q: %w" , objectID .String (), err )
4767 }
48- if pgpsig {
49- if len (line ) > 0 && line [0 ] == ' ' {
50- _ , _ = signatureSB .Write (line [1 :])
51- continue
52- }
53- pgpsig = false
68+ if len (line ) == 0 {
69+ break
5470 }
5571
56- if ! message {
57- // This is probably not correct but is copied from go-gits interpretation...
58- trimmed := bytes .TrimSpace (line )
59- if len (trimmed ) == 0 {
60- message = true
61- _ , _ = payloadSB . Write ( line )
62- continue
63- }
64-
65- split := bytes . SplitN ( trimmed , [] byte { ' ' }, 2 )
66- var data [] byte
67- if len ( split ) > 1 {
68- data = split [ 1 ]
72+ if inHeader {
73+ inHeader = ! ( len ( line ) == 1 && line [ 0 ] == '\n' ) // still in header if line is not just a newline
74+ k , v , _ := bytes .Cut (line , [] byte { ' ' } )
75+ if len (k ) != 0 || ! inHeader {
76+ if headerKey != "" {
77+ if err = assignCommitFields ( gitRepo , commit , headerKey , headerValue ); err != nil {
78+ return nil , fmt . Errorf ( "unable to parse commit %q: %w" , objectID . String (), err )
79+ }
80+ }
81+ headerKey = string ( k ) // it also resets the headerValue to empty string if not inHeader
82+ headerValue = v
83+ } else {
84+ headerValue = append ( headerValue , v ... )
6985 }
70-
71- switch string (split [0 ]) {
72- case "tree" :
73- commit .Tree = * NewTree (gitRepo , MustIDFromString (string (data )))
86+ if headerKey != commitHeaderGpgsig && headerKey != commitHeaderGpgsigSha256 {
7487 _ , _ = payloadSB .Write (line )
75- case "parent" :
76- commit .Parents = append (commit .Parents , MustIDFromString (string (data )))
77- _ , _ = payloadSB .Write (line )
78- case "author" :
79- commit .Author = & Signature {}
80- commit .Author .Decode (data )
81- _ , _ = payloadSB .Write (line )
82- case "committer" :
83- commit .Committer = & Signature {}
84- commit .Committer .Decode (data )
85- _ , _ = payloadSB .Write (line )
86- case "encoding" :
87- _ , _ = payloadSB .Write (line )
88- case "gpgsig" :
89- fallthrough
90- case "gpgsig-sha256" : // FIXME: no intertop, so only 1 exists at present.
91- _ , _ = signatureSB .Write (data )
92- _ = signatureSB .WriteByte ('\n' )
93- pgpsig = true
9488 }
9589 } else {
9690 _ , _ = messageSB .Write (line )
9791 _ , _ = payloadSB .Write (line )
9892 }
9993 }
94+
10095 commit .CommitMessage = messageSB .String ()
101- commit .Signature = & CommitSignature {
102- Signature : signatureSB .String (),
103- Payload : payloadSB .String (),
104- }
105- if len (commit .Signature .Signature ) == 0 {
106- commit .Signature = nil
96+ if commit .Signature != nil {
97+ commit .Signature .Payload = payloadSB .String ()
10798 }
108-
10999 return commit , nil
110100}
0 commit comments