git-lfs/lfs/gitscanner_tree.go

121 lines
2.9 KiB
Go
Raw Normal View History

2016-11-17 22:54:05 +00:00
package lfs
import (
"fmt"
"io/ioutil"
"github.com/git-lfs/git-lfs/config"
"github.com/git-lfs/git-lfs/filepathfilter"
"github.com/git-lfs/git-lfs/git"
2016-11-17 22:54:05 +00:00
)
func runScanTree(cb GitScannerFoundPointer, ref string, filter *filepathfilter.Filter, gitEnv, osEnv config.Environment) error {
2016-11-17 22:54:05 +00:00
// We don't use the nameMap approach here since that's imprecise when >1 file
// can be using the same content
treeShas, err := lsTreeBlobs(ref, filter)
2016-11-17 22:54:05 +00:00
if err != nil {
return err
}
pcw, err := catFileBatchTree(treeShas, gitEnv, osEnv)
if err != nil {
return err
2016-11-17 22:54:05 +00:00
}
for p := range pcw.Results {
cb(p, nil)
}
if err := pcw.Wait(); err != nil {
cb(nil, err)
}
return nil
2016-11-17 22:54:05 +00:00
}
// catFileBatchTree uses git cat-file --batch to get the object contents
// of a git object, given its sha1. The contents will be decoded into
// a Git LFS pointer. treeblobs is a channel over which blob entries
// will be sent. It returns a channel from which point.Pointers can be read.
func catFileBatchTree(treeblobs *TreeBlobChannelWrapper, gitEnv, osEnv config.Environment) (*PointerChannelWrapper, error) {
scanner, err := NewPointerScanner(gitEnv, osEnv)
2016-11-17 22:54:05 +00:00
if err != nil {
return nil, err
}
pointers := make(chan *WrappedPointer, chanBufSize)
errchan := make(chan error, 10) // Multiple errors possible
go func() {
hasNext := true
2016-11-17 22:54:05 +00:00
for t := range treeblobs.Results {
hasNext = scanner.Scan(t.Oid)
2016-11-19 00:00:57 +00:00
if p := scanner.Pointer(); p != nil {
2016-11-18 21:20:51 +00:00
p.Name = t.Filename
pointers <- p
2016-11-17 22:54:05 +00:00
}
2016-11-19 00:00:57 +00:00
if err := scanner.Err(); err != nil {
errchan <- err
}
if !hasNext {
break
}
2016-11-17 22:54:05 +00:00
}
2016-11-18 21:20:51 +00:00
// If the scanner quit early, we may still have treeblobs to
// read, so waiting for it to close will cause a deadlock.
if hasNext {
// Deal with nested error from incoming treeblobs
err := treeblobs.Wait()
if err != nil {
errchan <- err
}
2016-11-17 22:54:05 +00:00
}
2017-03-22 21:15:02 +00:00
if err = scanner.Close(); err != nil {
errchan <- err
2016-11-17 22:54:05 +00:00
}
2017-03-22 21:15:02 +00:00
2016-11-17 22:54:05 +00:00
close(pointers)
close(errchan)
}()
return NewPointerChannelWrapper(pointers, errchan), nil
}
// Use ls-tree at ref to find a list of candidate tree blobs which might be lfs files
// The returned channel will be sent these blobs which should be sent to catFileBatchTree
// for final check & conversion to Pointer
func lsTreeBlobs(ref string, filter *filepathfilter.Filter) (*TreeBlobChannelWrapper, error) {
cmd, err := git.LsTree(ref)
2016-11-17 22:54:05 +00:00
if err != nil {
return nil, err
}
cmd.Stdin.Close()
blobs := make(chan git.TreeBlob, chanBufSize)
2016-11-17 22:54:05 +00:00
errchan := make(chan error, 1)
go func() {
scanner := git.NewLsTreeScanner(cmd.Stdout)
for scanner.Scan() {
if t := scanner.TreeBlob(); t != nil && t.Size < blobSizeCutoff && filter.Allows(t.Filename) {
blobs <- *t
}
}
2016-11-17 22:54:05 +00:00
stderr, _ := ioutil.ReadAll(cmd.Stderr)
err := cmd.Wait()
if err != nil {
errchan <- fmt.Errorf("error in git ls-tree: %v %v", err, string(stderr))
2016-11-17 22:54:05 +00:00
}
close(blobs)
close(errchan)
}()
return NewTreeBlobChannelWrapper(blobs, errchan), nil
}