github.com/hawser/git-hawser@v2.5.2+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  		return err
    23  	}
    24  
    25  	go func() {
    26  		for r := range revs.Results {
    27  			canScan := scanner.Scan(r)
    28  
    29  			if err := scanner.Err(); err != nil {
    30  				errCh <- err
    31  			} else if p := scanner.Pointer(); p != nil {
    32  				pointerCh <- p
    33  			} else if b := scanner.BlobSHA(); len(b) == 40 {
    34  				if name, ok := lockableSet.Check(b); ok {
    35  					lockableCh <- name
    36  				}
    37  			}
    38  
    39  			if !canScan {
    40  				break
    41  			}
    42  		}
    43  
    44  		if err := revs.Wait(); err != nil {
    45  			errCh <- err
    46  		}
    47  
    48  		if err := scanner.Close(); err != nil {
    49  			errCh <- err
    50  		}
    51  
    52  		close(pointerCh)
    53  		close(errCh)
    54  		close(lockableCh)
    55  	}()
    56  
    57  	return nil
    58  }
    59  
    60  type PointerScanner struct {
    61  	scanner *git.ObjectScanner
    62  
    63  	blobSha     string
    64  	contentsSha string
    65  	pointer     *WrappedPointer
    66  	err         error
    67  }
    68  
    69  func NewPointerScanner() (*PointerScanner, error) {
    70  	scanner, err := git.NewObjectScanner()
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  
    75  	return &PointerScanner{scanner: scanner}, nil
    76  }
    77  
    78  func (s *PointerScanner) BlobSHA() string {
    79  	return s.blobSha
    80  }
    81  
    82  func (s *PointerScanner) ContentsSha() string {
    83  	return s.contentsSha
    84  }
    85  
    86  func (s *PointerScanner) Pointer() *WrappedPointer {
    87  	return s.pointer
    88  }
    89  
    90  func (s *PointerScanner) Err() error {
    91  	return s.err
    92  }
    93  
    94  func (s *PointerScanner) Scan(sha string) bool {
    95  	s.pointer, s.err = nil, nil
    96  	s.blobSha, s.contentsSha = "", ""
    97  
    98  	b, c, p, err := s.next(sha)
    99  	s.blobSha = b
   100  	s.contentsSha = c
   101  	s.pointer = p
   102  
   103  	if err != nil {
   104  		if err != io.EOF {
   105  			s.err = err
   106  		}
   107  		return false
   108  	}
   109  
   110  	return true
   111  }
   112  
   113  func (s *PointerScanner) Close() error {
   114  	return s.scanner.Close()
   115  }
   116  
   117  func (s *PointerScanner) next(blob string) (string, string, *WrappedPointer, error) {
   118  	if !s.scanner.Scan(blob) {
   119  		if err := s.scanner.Err(); err != nil {
   120  			return "", "", nil, err
   121  		}
   122  		return "", "", nil, io.EOF
   123  	}
   124  
   125  	blobSha := s.scanner.Sha1()
   126  	size := s.scanner.Size()
   127  
   128  	sha := sha256.New()
   129  
   130  	var buf *bytes.Buffer
   131  	var to io.Writer = sha
   132  	if size <= blobSizeCutoff {
   133  		buf = bytes.NewBuffer(make([]byte, 0, size))
   134  		to = io.MultiWriter(to, buf)
   135  	}
   136  
   137  	read, err := io.CopyN(to, s.scanner.Contents(), int64(size))
   138  	if err != nil {
   139  		return blobSha, "", nil, err
   140  	}
   141  
   142  	if int64(size) != read {
   143  		return blobSha, "", nil, fmt.Errorf("expected %d bytes, read %d bytes", size, read)
   144  	}
   145  
   146  	var pointer *WrappedPointer
   147  	var contentsSha string
   148  
   149  	if size <= blobSizeCutoff {
   150  		if p, err := DecodePointer(bytes.NewReader(buf.Bytes())); err != nil {
   151  			contentsSha = fmt.Sprintf("%x", sha.Sum(nil))
   152  		} else {
   153  			pointer = &WrappedPointer{
   154  				Sha1:    blobSha,
   155  				Pointer: p,
   156  			}
   157  			contentsSha = p.Oid
   158  		}
   159  	} else {
   160  		contentsSha = fmt.Sprintf("%x", sha.Sum(nil))
   161  	}
   162  
   163  	return blobSha, contentsSha, pointer, err
   164  }