Imported Upstream version 2.3.4
[scm/test.git] / lfs / gitscanner_catfilebatchcheck.go
1 package lfs
2
3 import (
4         "bufio"
5         "fmt"
6         "io/ioutil"
7         "strconv"
8
9         "github.com/git-lfs/git-lfs/git"
10 )
11
12 // runCatFileBatchCheck uses 'git cat-file --batch-check' to get the type and
13 // size of a git object. Any object that isn't of type blob and under the
14 // blobSizeCutoff will be ignored, unless it's a locked file. revs is a channel
15 // over which strings containing git sha1s will be sent. It returns a channel
16 // from which sha1 strings can be read.
17 func runCatFileBatchCheck(smallRevCh chan string, lockableCh chan string, lockableSet *lockableNameSet, revs *StringChannelWrapper, errCh chan error) error {
18         cmd, err := git.CatFile()
19         if err != nil {
20                 return err
21         }
22
23         go func() {
24                 scanner := &catFileBatchCheckScanner{s: bufio.NewScanner(cmd.Stdout), limit: blobSizeCutoff}
25                 for r := range revs.Results {
26                         cmd.Stdin.Write([]byte(r + "\n"))
27                         hasNext := scanner.Scan()
28                         if err := scanner.Err(); err != nil {
29                                 errCh <- err
30                         } else if b := scanner.LFSBlobOID(); len(b) > 0 {
31                                 smallRevCh <- b
32                         } else if b := scanner.GitBlobOID(); len(b) > 0 {
33                                 if name, ok := lockableSet.Check(b); ok {
34                                         lockableCh <- name
35                                 }
36                         }
37
38                         if !hasNext {
39                                 break
40                         }
41                 }
42
43                 if err := revs.Wait(); err != nil {
44                         errCh <- err
45                 }
46                 cmd.Stdin.Close()
47
48                 stderr, _ := ioutil.ReadAll(cmd.Stderr)
49                 err := cmd.Wait()
50                 if err != nil {
51                         errCh <- fmt.Errorf("Error in git cat-file --batch-check: %v %v", err, string(stderr))
52                 }
53                 close(smallRevCh)
54                 close(errCh)
55         }()
56
57         return nil
58 }
59
60 type catFileBatchCheckScanner struct {
61         s          *bufio.Scanner
62         limit      int
63         lfsBlobOID string
64         gitBlobOID string
65 }
66
67 func (s *catFileBatchCheckScanner) LFSBlobOID() string {
68         return s.lfsBlobOID
69 }
70
71 func (s *catFileBatchCheckScanner) GitBlobOID() string {
72         return s.gitBlobOID
73 }
74
75 func (s *catFileBatchCheckScanner) Err() error {
76         return s.s.Err()
77 }
78
79 func (s *catFileBatchCheckScanner) Scan() bool {
80         lfsBlobSha, gitBlobSha, hasNext := s.next()
81         s.lfsBlobOID = lfsBlobSha
82         s.gitBlobOID = gitBlobSha
83         return hasNext
84 }
85
86 func (s *catFileBatchCheckScanner) next() (string, string, bool) {
87         hasNext := s.s.Scan()
88         line := s.s.Text()
89         lineLen := len(line)
90
91         // Format is:
92         // <sha1> <type> <size>
93         // type is at a fixed spot, if we see that it's "blob", we can avoid
94         // splitting the line just to get the size.
95         if lineLen < 46 {
96                 return "", "", hasNext
97         }
98
99         if line[41:45] != "blob" {
100                 return "", "", hasNext
101         }
102
103         size, err := strconv.Atoi(line[46:lineLen])
104         if err != nil {
105                 return "", "", hasNext
106         }
107
108         blobSha := line[0:40]
109         if size >= s.limit {
110                 return "", blobSha, hasNext
111         }
112
113         return blobSha, "", hasNext
114 }