@@ -22,70 +22,72 @@ func ParseTreeEntries(data []byte) ([]*TreeEntry, error) {
2222 return parseTreeEntries (data , nil )
2323}
2424
25+ var sepSpace = []byte {' ' }
26+
2527func parseTreeEntries (data []byte , ptree * Tree ) ([]* TreeEntry , error ) {
26- entries := make ([]* TreeEntry , 0 , 10 )
28+ var err error
29+ entries := make ([]* TreeEntry , 0 , bytes .Count (data , []byte {'\n' })+ 1 )
2730 for pos := 0 ; pos < len (data ); {
28- // expect line to be of the form "<mode> <type> <sha> <space-padded-size>\t<filename>"
31+ // expect line to be of the form:
32+ // <mode> <type> <sha> <space-padded-size>\t<filename>
33+ // <mode> <type> <sha>\t<filename>
34+ posEnd := bytes .IndexByte (data [pos :], '\n' )
35+ if posEnd == - 1 {
36+ posEnd = len (data )
37+ } else {
38+ posEnd += pos
39+ }
40+ line := data [pos :posEnd ]
41+ posTab := bytes .IndexByte (line , '\t' )
42+ if posTab == - 1 {
43+ return nil , fmt .Errorf ("invalid ls-tree output (no tab): %q" , line )
44+ }
45+
2946 entry := new (TreeEntry )
3047 entry .ptree = ptree
31- if pos + 6 > len (data ) {
32- return nil , fmt .Errorf ("Invalid ls-tree output: %s" , string (data ))
48+
49+ entryAttrs := line [:posTab ]
50+ entryName := line [posTab + 1 :]
51+
52+ entryMode , entryAttrs , _ := bytes .Cut (entryAttrs , sepSpace )
53+ _ /* entryType */ , entryAttrs , _ = bytes .Cut (entryAttrs , sepSpace ) // the type is not used, the mode is enough to determine the type
54+ entryObjectID , entryAttrs , _ := bytes .Cut (entryAttrs , sepSpace )
55+ if len (entryAttrs ) > 0 {
56+ entrySize := entryAttrs // the last field is the space-padded-size
57+ entry .size , _ = strconv .ParseInt (strings .TrimSpace (string (entrySize )), 10 , 64 )
58+ entry .sized = true
3359 }
34- switch string (data [pos : pos + 6 ]) {
60+
61+ switch string (entryMode ) {
3562 case "100644" :
3663 entry .entryMode = EntryModeBlob
37- pos += 12 // skip over "100644 blob "
3864 case "100755" :
3965 entry .entryMode = EntryModeExec
40- pos += 12 // skip over "100755 blob "
4166 case "120000" :
4267 entry .entryMode = EntryModeSymlink
43- pos += 12 // skip over "120000 blob "
4468 case "160000" :
4569 entry .entryMode = EntryModeCommit
46- pos += 14 // skip over "160000 object "
4770 case "040000" , "040755" : // git uses 040000 for tree object, but some users may get 040755 for unknown reasons
4871 entry .entryMode = EntryModeTree
49- pos += 12 // skip over "040000 tree "
5072 default :
51- return nil , fmt .Errorf ("unknown type: %v" , string (data [ pos : pos + 6 ] ))
73+ return nil , fmt .Errorf ("unknown type: %v" , string (entryMode ))
5274 }
5375
54- if pos + 40 > len (data ) {
55- return nil , fmt .Errorf ("Invalid ls-tree output: %s" , string (data ))
56- }
57- id , err := NewIDFromString (string (data [pos : pos + 40 ]))
76+ entry .ID , err = NewIDFromString (string (entryObjectID ))
5877 if err != nil {
59- return nil , fmt .Errorf ("Invalid ls-tree output: %v" , err )
60- }
61- entry .ID = id
62- pos += 41 // skip over sha and trailing space
63-
64- end := pos + bytes .IndexByte (data [pos :], '\t' )
65- if end < pos {
66- return nil , fmt .Errorf ("Invalid ls-tree -l output: %s" , string (data ))
67- }
68- entry .size , _ = strconv .ParseInt (strings .TrimSpace (string (data [pos :end ])), 10 , 64 )
69- entry .sized = true
70-
71- pos = end + 1
72-
73- end = pos + bytes .IndexByte (data [pos :], '\n' )
74- if end < pos {
75- return nil , fmt .Errorf ("Invalid ls-tree output: %s" , string (data ))
78+ return nil , fmt .Errorf ("invalid ls-tree output (invalid object id): %q, err: %w" , line , err )
7679 }
7780
78- // In case entry name is surrounded by double quotes(it happens only in git-shell).
79- if data [pos ] == '"' {
80- entry .name , err = strconv .Unquote (string (data [pos :end ]))
81+ if len (entryName ) > 0 && entryName [0 ] == '"' {
82+ entry .name , err = strconv .Unquote (string (entryName ))
8183 if err != nil {
82- return nil , fmt .Errorf ("Invalid ls-tree output: %v" , err )
84+ return nil , fmt .Errorf ("invalid ls-tree output (invalid name) : %q, err: %w" , line , err )
8385 }
8486 } else {
85- entry .name = string (data [ pos : end ] )
87+ entry .name = string (entryName )
8688 }
8789
88- pos = end + 1
90+ pos = posEnd + 1
8991 entries = append (entries , entry )
9092 }
9193 return entries , nil
0 commit comments