github.com/psexton/git-lfs@v2.1.1-0.20170517224304-289a18b2bc53+incompatible/lfs/gitscanner_catfilebatchcheck.go (about)

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