@@ -6,10 +6,44 @@ package git
6
6
import (
7
7
"bufio"
8
8
"bytes"
9
+ "fmt"
9
10
"io"
10
- "strings"
11
11
)
12
12
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
+
13
47
// CommitFromReader will generate a Commit from a provided reader
14
48
// We need this to interpret commits from cat-file or cat-file --batch
15
49
//
@@ -21,90 +55,46 @@ func CommitFromReader(gitRepo *Repository, objectID ObjectID, reader io.Reader)
21
55
Committer : & Signature {},
22
56
}
23
57
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
36
63
for {
37
64
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 )
47
67
}
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
54
70
}
55
71
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 ... )
69
85
}
70
-
71
- switch string (split [0 ]) {
72
- case "tree" :
73
- commit .Tree = * NewTree (gitRepo , MustIDFromString (string (data )))
86
+ if headerKey != commitHeaderGpgsig && headerKey != commitHeaderGpgsigSha256 {
74
87
_ , _ = 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
94
88
}
95
89
} else {
96
90
_ , _ = messageSB .Write (line )
97
91
_ , _ = payloadSB .Write (line )
98
92
}
99
93
}
94
+
100
95
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 ()
107
98
}
108
-
109
99
return commit , nil
110
100
}
0 commit comments