github.com/git-lfs/git-lfs@v2.5.2+incompatible/lfs/gitscanner_refs.go (about) 1 package lfs 2 3 import ( 4 "encoding/hex" 5 "regexp" 6 7 "github.com/git-lfs/git-lfs/git" 8 ) 9 10 var z40 = regexp.MustCompile(`\^?0{40}`) 11 12 type lockableNameSet struct { 13 opt *ScanRefsOptions 14 set GitScannerSet 15 } 16 17 // Determines if the given blob sha matches a locked file. 18 func (s *lockableNameSet) Check(blobSha string) (string, bool) { 19 if s == nil || s.opt == nil || s.set == nil { 20 return "", false 21 } 22 23 name, ok := s.opt.GetName(blobSha) 24 if !ok { 25 return name, ok 26 } 27 28 if s.set.Contains(name) { 29 return name, true 30 } 31 return name, false 32 } 33 34 func noopFoundLockable(name string) {} 35 36 // scanRefsToChan scans through all commits reachable by refs contained in 37 // "include" and not reachable by any refs included in "excluded" and returns 38 // a channel of WrappedPointer objects for all Git LFS pointers it finds. 39 // Reports unique oids once only, not multiple times if >1 file uses the same content 40 func scanRefsToChan(scanner *GitScanner, pointerCb GitScannerFoundPointer, include, exclude []string, opt *ScanRefsOptions) error { 41 if opt == nil { 42 panic("no scan ref options") 43 } 44 45 revs, err := revListShas(include, exclude, opt) 46 if err != nil { 47 return err 48 } 49 50 lockableSet := &lockableNameSet{opt: opt, set: scanner.PotentialLockables} 51 smallShas, batchLockableCh, err := catFileBatchCheck(revs, lockableSet) 52 if err != nil { 53 return err 54 } 55 56 lockableCb := scanner.FoundLockable 57 if lockableCb == nil { 58 lockableCb = noopFoundLockable 59 } 60 61 go func(cb GitScannerFoundLockable, ch chan string) { 62 for name := range ch { 63 cb(name) 64 } 65 }(lockableCb, batchLockableCh) 66 67 pointers, checkLockableCh, err := catFileBatch(smallShas, lockableSet) 68 if err != nil { 69 return err 70 } 71 72 for p := range pointers.Results { 73 if name, ok := opt.GetName(p.Sha1); ok { 74 p.Name = name 75 } 76 77 if scanner.Filter.Allows(p.Name) { 78 pointerCb(p, nil) 79 } 80 } 81 82 for lockableName := range checkLockableCh { 83 if scanner.Filter.Allows(lockableName) { 84 lockableCb(lockableName) 85 } 86 } 87 88 if err := pointers.Wait(); err != nil { 89 pointerCb(nil, err) 90 } 91 92 return nil 93 } 94 95 // scanLeftRightToChan takes a ref and returns a channel of WrappedPointer objects 96 // for all Git LFS pointers it finds for that ref. 97 // Reports unique oids once only, not multiple times if >1 file uses the same content 98 func scanLeftRightToChan(scanner *GitScanner, pointerCb GitScannerFoundPointer, refLeft, refRight string, opt *ScanRefsOptions) error { 99 return scanRefsToChan(scanner, pointerCb, []string{refLeft, refRight}, nil, opt) 100 } 101 102 // revListShas uses git rev-list to return the list of object sha1s 103 // for the given ref. If all is true, ref is ignored. It returns a 104 // channel from which sha1 strings can be read. 105 func revListShas(include, exclude []string, opt *ScanRefsOptions) (*StringChannelWrapper, error) { 106 scanner, err := git.NewRevListScanner(include, exclude, &git.ScanRefsOptions{ 107 Mode: git.ScanningMode(opt.ScanMode), 108 Remote: opt.RemoteName, 109 SkipDeletedBlobs: opt.SkipDeletedBlobs, 110 SkippedRefs: opt.skippedRefs, 111 Mutex: opt.mutex, 112 Names: opt.nameMap, 113 }) 114 115 if err != nil { 116 return nil, err 117 } 118 119 revs := make(chan string, chanBufSize) 120 errs := make(chan error, 5) // may be multiple errors 121 122 go func() { 123 for scanner.Scan() { 124 sha := hex.EncodeToString(scanner.OID()) 125 if name := scanner.Name(); len(name) > 0 { 126 opt.SetName(sha, name) 127 } 128 revs <- sha 129 } 130 131 if err = scanner.Err(); err != nil { 132 errs <- err 133 } 134 135 if err = scanner.Close(); err != nil { 136 errs <- err 137 } 138 139 close(revs) 140 close(errs) 141 }() 142 143 return NewStringChannelWrapper(revs, errs), nil 144 }