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 }