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

     1  package lfs
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/sha256"
     6  	"fmt"
     7  	"io"
     8  
     9  	"github.com/git-lfs/git-lfs/git"
    10  )
    11  
    12  // runCatFileBatch uses 'git cat-file --batch' to get the object contents of a
    13  // git object, given its sha1. The contents will be decoded into a Git LFS
    14  // pointer. Git Blob SHA1s are read from the sha1Ch channel and fed to STDIN.
    15  // Results are parsed from STDOUT, and any eligible LFS pointers are sent to
    16  // pointerCh. If a Git Blob is not an LFS pointer, check the lockableSet to see
    17  // if that blob is for a locked file. Any errors are sent to errCh. An error is
    18  // returned if the 'git cat-file' command fails to start.
    19  func runCatFileBatch(pointerCh chan *WrappedPointer, lockableCh chan string, lockableSet *lockableNameSet, revs *StringChannelWrapper, errCh chan error) error {
    20  	scanner, err := NewPointerScanner()
    21  	if err != nil {
    22  		scanner.Close()
    23  
    24  		return err
    25  	}
    26  
    27  	go func() {
    28  		for r := range revs.Results {
    29  			canScan := scanner.Scan(r)
    30  
    31  			if err := scanner.Err(); err != nil {
    32  				errCh <- err
    33  			} else if p := scanner.Pointer(); p != nil {
    34  				pointerCh <- p
    35  			} else if b := scanner.BlobSHA(); len(b) == 40 {
    36  				if name, ok := lockableSet.Check(b); ok {
    37  					lockableCh <- name
    38  				}
    39  			}
    40  
    41  			if !canScan {
    42  				break
    43  			}
    44  		}
    45  
    46  		if err := revs.Wait(); err != nil {
    47  			errCh <- err
    48  		}
    49  
    50  		if err := scanner.Close(); err != nil {
    51  			errCh <- err
    52  		}
    53  
    54  		close(pointerCh)
    55  		close(errCh)
    56  		close(lockableCh)
    57  	}()
    58  
    59  	return nil
    60  }
    61  
    62  type PointerScanner struct {
    63  	scanner *git.ObjectScanner
    64  
    65  	blobSha     string
    66  	contentsSha string
    67  	pointer     *WrappedPointer
    68  	err         error
    69  }
    70  
    71  func NewPointerScanner() (*PointerScanner, error) {
    72  	scanner, err := git.NewObjectScanner()
    73  	if err != nil {
    74  		return nil, err
    75  	}
    76  
    77  	return &PointerScanner{scanner: scanner}, nil
    78  }
    79  
    80  func (s *PointerScanner) BlobSHA() string {
    81  	return s.blobSha
    82  }
    83  
    84  func (s *PointerScanner) ContentsSha() string {
    85  	return s.contentsSha
    86  }
    87  
    88  func (s *PointerScanner) Pointer() *WrappedPointer {
    89  	return s.pointer
    90  }
    91  
    92  func (s *PointerScanner) Err() error {
    93  	return s.err
    94  }
    95  
    96  func (s *PointerScanner) Scan(sha string) bool {
    97  	s.pointer, s.err = nil, nil
    98  	s.blobSha, s.contentsSha = "", ""
    99  
   100  	b, c, p, err := s.next(sha)
   101  	s.blobSha = b
   102  	s.contentsSha = c
   103  	s.pointer = p
   104  
   105  	if err != nil {
   106  		if err != io.EOF {
   107  			s.err = err
   108  		}
   109  		return false
   110  	}
   111  
   112  	return true
   113  }
   114  
   115  func (s *PointerScanner) Close() error {
   116  	return s.scanner.Close()
   117  }
   118  
   119  func (s *PointerScanner) next(blob string) (string, string, *WrappedPointer, error) {
   120  	if !s.scanner.Scan(blob) {
   121  		if err := s.scanner.Err(); err != nil {
   122  			return "", "", nil, err
   123  		}
   124  		return "", "", nil, io.EOF
   125  	}
   126  
   127  	blobSha := s.scanner.Sha1()
   128  	size := s.scanner.Size()
   129  
   130  	sha := sha256.New()
   131  
   132  	var buf *bytes.Buffer
   133  	var to io.Writer = sha
   134  	if size <= blobSizeCutoff {
   135  		buf = bytes.NewBuffer(make([]byte, 0, size))
   136  		to = io.MultiWriter(to, buf)
   137  	}
   138  
   139  	read, err := io.CopyN(to, s.scanner.Contents(), int64(size))
   140  	if err != nil {
   141  		return blobSha, "", nil, err
   142  	}
   143  
   144  	if int64(size) != read {
   145  		return blobSha, "", nil, fmt.Errorf("expected %d bytes, read %d bytes", size, read)
   146  	}
   147  
   148  	var pointer *WrappedPointer
   149  	var contentsSha string
   150  
   151  	if size <= blobSizeCutoff {
   152  		if p, err := DecodePointer(bytes.NewReader(buf.Bytes())); err != nil {
   153  			contentsSha = fmt.Sprintf("%x", sha.Sum(nil))
   154  		} else {
   155  			pointer = &WrappedPointer{
   156  				Sha1:    blobSha,
   157  				Pointer: p,
   158  			}
   159  			contentsSha = p.Oid
   160  		}
   161  	} else {
   162  		contentsSha = fmt.Sprintf("%x", sha.Sum(nil))
   163  	}
   164  
   165  	return blobSha, contentsSha, pointer, err
   166  }