@@ -22,70 +22,72 @@ func ParseTreeEntries(data []byte) ([]*TreeEntry, error) {
22
22
return parseTreeEntries (data , nil )
23
23
}
24
24
25
+ var sepSpace = []byte {' ' }
26
+
25
27
func 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 )
27
30
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
+
29
46
entry := new (TreeEntry )
30
47
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
33
59
}
34
- switch string (data [pos : pos + 6 ]) {
60
+
61
+ switch string (entryMode ) {
35
62
case "100644" :
36
63
entry .entryMode = EntryModeBlob
37
- pos += 12 // skip over "100644 blob "
38
64
case "100755" :
39
65
entry .entryMode = EntryModeExec
40
- pos += 12 // skip over "100755 blob "
41
66
case "120000" :
42
67
entry .entryMode = EntryModeSymlink
43
- pos += 12 // skip over "120000 blob "
44
68
case "160000" :
45
69
entry .entryMode = EntryModeCommit
46
- pos += 14 // skip over "160000 object "
47
70
case "040000" , "040755" : // git uses 040000 for tree object, but some users may get 040755 for unknown reasons
48
71
entry .entryMode = EntryModeTree
49
- pos += 12 // skip over "040000 tree "
50
72
default :
51
- return nil , fmt .Errorf ("unknown type: %v" , string (data [ pos : pos + 6 ] ))
73
+ return nil , fmt .Errorf ("unknown type: %v" , string (entryMode ))
52
74
}
53
75
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 ))
58
77
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 )
76
79
}
77
80
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 ))
81
83
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 )
83
85
}
84
86
} else {
85
- entry .name = string (data [ pos : end ] )
87
+ entry .name = string (entryName )
86
88
}
87
89
88
- pos = end + 1
90
+ pos = posEnd + 1
89
91
entries = append (entries , entry )
90
92
}
91
93
return entries , nil
0 commit comments