247799dc37
Git will never try to filter a symlink, since it is not a regular file. Extract the file mode from git ls-tree and verify that the blob we're seeing is a regular file, whether executable or not, and not a symlink.
94 lines
1.7 KiB
Go
94 lines
1.7 KiB
Go
package git
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"io"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// An entry from ls-tree or rev-list including a blob sha and tree path
|
|
type TreeBlob struct {
|
|
Oid string
|
|
Size int64
|
|
Mode int32
|
|
Filename string
|
|
}
|
|
|
|
type LsTreeScanner struct {
|
|
s *bufio.Scanner
|
|
tree *TreeBlob
|
|
}
|
|
|
|
func NewLsTreeScanner(r io.Reader) *LsTreeScanner {
|
|
s := bufio.NewScanner(r)
|
|
s.Split(scanNullLines)
|
|
return &LsTreeScanner{s: s}
|
|
}
|
|
|
|
func (s *LsTreeScanner) TreeBlob() *TreeBlob {
|
|
return s.tree
|
|
}
|
|
|
|
func (s *LsTreeScanner) Err() error {
|
|
return nil
|
|
}
|
|
|
|
func (s *LsTreeScanner) Scan() bool {
|
|
t, hasNext := s.next()
|
|
s.tree = t
|
|
return hasNext
|
|
}
|
|
|
|
func (s *LsTreeScanner) next() (*TreeBlob, bool) {
|
|
hasNext := s.s.Scan()
|
|
line := s.s.Text()
|
|
parts := strings.SplitN(line, "\t", 2)
|
|
if len(parts) < 2 {
|
|
return nil, hasNext
|
|
}
|
|
|
|
attrs := strings.SplitN(parts[0], " ", 4)
|
|
if len(attrs) < 4 {
|
|
return nil, hasNext
|
|
}
|
|
|
|
mode, err := strconv.ParseInt(strings.TrimSpace(attrs[0]), 8, 32)
|
|
if err != nil {
|
|
return nil, hasNext
|
|
}
|
|
|
|
if attrs[1] != "blob" {
|
|
return nil, hasNext
|
|
}
|
|
|
|
sz, err := strconv.ParseInt(strings.TrimSpace(attrs[3]), 10, 64)
|
|
if err != nil {
|
|
return nil, hasNext
|
|
}
|
|
|
|
oid := attrs[2]
|
|
filename := parts[1]
|
|
return &TreeBlob{Oid: oid, Size: sz, Mode: int32(mode), Filename: filename}, hasNext
|
|
}
|
|
|
|
func scanNullLines(data []byte, atEOF bool) (advance int, token []byte, err error) {
|
|
if atEOF && len(data) == 0 {
|
|
return 0, nil, nil
|
|
}
|
|
|
|
if i := bytes.IndexByte(data, '\000'); i >= 0 {
|
|
// We have a full null-terminated line.
|
|
return i + 1, data[0:i], nil
|
|
}
|
|
|
|
// If we're at EOF, we have a final, non-terminated line. Return it.
|
|
if atEOF {
|
|
return len(data), data, nil
|
|
}
|
|
|
|
// Request more data.
|
|
return 0, nil, nil
|
|
}
|