@@ -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