@@ -13,9 +13,44 @@ import (
1313 "strings"
1414)
1515
16+ // WriteCloserError wraps an io.WriteCloser with an additional CloseWithError function
17+ type WriteCloserError interface {
18+ io.WriteCloser
19+ CloseWithError (err error ) error
20+ }
21+
22+ // CatFileBatchCheck opens git cat-file --batch-check in the provided repo and returns a stdin pipe, a stdout reader and cancel function
23+ func CatFileBatchCheck (repoPath string ) (WriteCloserError , * bufio.Reader , func ()) {
24+ batchStdinReader , batchStdinWriter := io .Pipe ()
25+ batchStdoutReader , batchStdoutWriter := io .Pipe ()
26+ cancel := func () {
27+ _ = batchStdinReader .Close ()
28+ _ = batchStdinWriter .Close ()
29+ _ = batchStdoutReader .Close ()
30+ _ = batchStdoutWriter .Close ()
31+ }
32+
33+ go func () {
34+ stderr := strings.Builder {}
35+ err := NewCommand ("cat-file" , "--batch-check" ).RunInDirFullPipeline (repoPath , batchStdoutWriter , & stderr , batchStdinReader )
36+ if err != nil {
37+ _ = batchStdoutWriter .CloseWithError (ConcatenateError (err , (& stderr ).String ()))
38+ _ = batchStdinReader .CloseWithError (ConcatenateError (err , (& stderr ).String ()))
39+ } else {
40+ _ = batchStdoutWriter .Close ()
41+ _ = batchStdinReader .Close ()
42+ }
43+ }()
44+
45+ // For simplicities sake we'll us a buffered reader to read from the cat-file --batch
46+ batchReader := bufio .NewReader (batchStdoutReader )
47+
48+ return batchStdinWriter , batchReader , cancel
49+ }
50+
1651// CatFileBatch opens git cat-file --batch in the provided repo and returns a stdin pipe, a stdout reader and cancel function
17- func CatFileBatch (repoPath string ) (* io. PipeWriter , * bufio.Reader , func ()) {
18- // Next feed the commits in order into cat-file --batch, followed by their trees and sub trees as necessary.
52+ func CatFileBatch (repoPath string ) (WriteCloserError , * bufio.Reader , func ()) {
53+ // We often want to feed the commits in order into cat-file --batch, followed by their trees and sub trees as necessary.
1954 // so let's create a batch stdin and stdout
2055 batchStdinReader , batchStdinWriter := io .Pipe ()
2156 batchStdoutReader , batchStdoutWriter := io .Pipe ()
@@ -47,26 +82,28 @@ func CatFileBatch(repoPath string) (*io.PipeWriter, *bufio.Reader, func()) {
4782// ReadBatchLine reads the header line from cat-file --batch
4883// We expect:
4984// <sha> SP <type> SP <size> LF
85+ // sha is a 40byte not 20byte here
5086func ReadBatchLine (rd * bufio.Reader ) (sha []byte , typ string , size int64 , err error ) {
5187 sha , err = rd .ReadBytes (' ' )
5288 if err != nil {
5389 return
5490 }
5591 sha = sha [:len (sha )- 1 ]
5692
57- typ , err = rd .ReadString (' ' )
93+ typ , err = rd .ReadString ('\n ' )
5894 if err != nil {
5995 return
6096 }
61- typ = typ [:len (typ )- 1 ]
6297
63- var sizeStr string
64- sizeStr , err = rd . ReadString ( '\n' )
65- if err != nil {
98+ idx := strings . Index ( typ , " " )
99+ if idx < 0 {
100+ err = ErrNotExist { ID : string ( sha )}
66101 return
67102 }
103+ sizeStr := typ [idx + 1 : len (typ )- 1 ]
104+ typ = typ [:idx ]
68105
69- size , err = strconv .ParseInt (sizeStr [: len ( sizeStr ) - 1 ] , 10 , 64 )
106+ size , err = strconv .ParseInt (sizeStr , 10 , 64 )
70107 return
71108}
72109
@@ -128,7 +165,7 @@ headerLoop:
128165 }
129166
130167 // Discard the rest of the commit
131- discard := size - n
168+ discard := size - n + 1
132169 for discard > math .MaxInt32 {
133170 _ , err := rd .Discard (math .MaxInt32 )
134171 if err != nil {
0 commit comments