github.com/git-lfs/git-lfs@v2.5.2+incompatible/lfs/gitscanner_catfilebatchcheck.go (about)

     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  }