git-lfs/lfs/gitscanner_refs.go

192 lines
5.2 KiB
Go
Raw Normal View History

2016-11-17 22:47:01 +00:00
package lfs
import (
2017-05-24 18:54:32 +00:00
"encoding/hex"
"sync"
2017-05-24 18:54:32 +00:00
"github.com/git-lfs/git-lfs/v3/config"
"github.com/git-lfs/git-lfs/v3/git"
2016-11-17 22:47:01 +00:00
)
type lockableNameSet struct {
opt *ScanRefsOptions
set GitScannerSet
}
// Determines if the given blob sha matches a locked file.
func (s *lockableNameSet) Check(blobSha string) (string, bool) {
if s == nil || s.opt == nil || s.set == nil {
return "", false
}
name, ok := s.opt.GetName(blobSha)
if !ok {
return name, ok
}
if s.set.Contains(name) {
return name, true
}
return name, false
}
func noopFoundLockable(name string) {}
// scanRefsToChan scans through all commits reachable by refs contained in
// "include" and not reachable by any refs included in "exclude" and invokes
// the provided callback for each pointer file, valid or invalid, that it finds.
2016-11-17 22:47:01 +00:00
// Reports unique oids once only, not multiple times if >1 file uses the same content
func scanRefsToChan(scanner *GitScanner, pointerCb GitScannerFoundPointer, include, exclude []string, gitEnv, osEnv config.Environment, opt *ScanRefsOptions) error {
2016-11-17 22:47:01 +00:00
if opt == nil {
panic("no scan ref options")
}
revs, err := revListShas(include, exclude, opt)
2016-11-17 22:47:01 +00:00
if err != nil {
return err
2016-11-17 22:47:01 +00:00
}
lockableSet := &lockableNameSet{opt: opt, set: scanner.PotentialLockables}
smallShas, batchLockableCh, err := catFileBatchCheck(revs, lockableSet)
2016-11-17 22:47:01 +00:00
if err != nil {
return err
2016-11-17 22:47:01 +00:00
}
lockableCb := scanner.FoundLockable
if lockableCb == nil {
lockableCb = noopFoundLockable
}
go func(cb GitScannerFoundLockable, ch chan string) {
for name := range ch {
cb(name)
}
}(lockableCb, batchLockableCh)
pointers, checkLockableCh, err := catFileBatch(smallShas, lockableSet, gitEnv, osEnv)
2016-11-17 22:47:01 +00:00
if err != nil {
return err
2016-11-17 22:47:01 +00:00
}
for p := range pointers.Results {
if name, ok := opt.GetName(p.Sha1); ok {
p.Name = name
2016-11-17 22:47:01 +00:00
}
if scanner.Filter.Allows(p.Name) {
pointerCb(p, nil)
}
}
for lockableName := range checkLockableCh {
if scanner.Filter.Allows(lockableName) {
lockableCb(lockableName)
}
}
if err := pointers.Wait(); err != nil {
pointerCb(nil, err)
}
2016-11-17 22:47:01 +00:00
return nil
2016-11-17 22:47:01 +00:00
}
// scanLeftRightToChan takes a ref and returns a channel of WrappedPointer objects
// for all Git LFS pointers it finds for that ref.
// Reports unique oids once only, not multiple times if >1 file uses the same content
func scanLeftRightToChan(scanner *GitScanner, pointerCb GitScannerFoundPointer, refLeft, refRight string, gitEnv, osEnv config.Environment, opt *ScanRefsOptions) error {
return scanRefsToChan(scanner, pointerCb, []string{refLeft}, []string{refRight}, gitEnv, osEnv, opt)
}
// scanMultiLeftRightToChan takes a ref and a set of bases and returns a channel
// of WrappedPointer objects for all Git LFS pointers it finds for that ref.
// Reports unique oids once only, not multiple times if >1 file uses the same
// content
func scanMultiLeftRightToChan(scanner *GitScanner, pointerCb GitScannerFoundPointer, refLeft string, bases []string, gitEnv, osEnv config.Environment, opt *ScanRefsOptions) error {
return scanRefsToChan(scanner, pointerCb, []string{refLeft}, bases, gitEnv, osEnv, opt)
}
// scanRefsByTree scans through all commits reachable by refs contained in
// "include" and not reachable by any refs included in "exclude" and invokes
// the provided callback for each pointer file, valid or invalid, that it finds.
// Reports unique oids once only, not multiple times if >1 file uses the same content
func scanRefsByTree(scanner *GitScanner, pointerCb GitScannerFoundPointer, include, exclude []string, gitEnv, osEnv config.Environment, opt *ScanRefsOptions) error {
if opt == nil {
panic("no scan ref options")
}
revs, err := revListShas(include, exclude, opt)
if err != nil {
return err
}
errchan := make(chan error, 20) // multiple errors possible
wg := &sync.WaitGroup{}
for r := range revs.Results {
wg.Add(1)
go func(rev string) {
defer wg.Done()
err := runScanTreeForPointers(pointerCb, rev, gitEnv, osEnv)
if err != nil {
errchan <- err
}
}(r)
}
wg.Wait()
close(errchan)
for err := range errchan {
if err != nil {
return err
}
}
return revs.Wait()
}
2016-11-17 22:47:01 +00:00
// revListShas uses git rev-list to return the list of object sha1s
// for the given ref. If all is true, ref is ignored. It returns a
// channel from which sha1 strings can be read.
func revListShas(include, exclude []string, opt *ScanRefsOptions) (*StringChannelWrapper, error) {
scanner, err := git.NewRevListScanner(include, exclude, &git.ScanRefsOptions{
2017-05-24 18:54:32 +00:00
Mode: git.ScanningMode(opt.ScanMode),
Remote: opt.RemoteName,
SkipDeletedBlobs: opt.SkipDeletedBlobs,
SkippedRefs: opt.skippedRefs,
Mutex: opt.mutex,
Names: opt.nameMap,
CommitsOnly: opt.CommitsOnly,
2017-05-24 18:54:32 +00:00
})
2016-11-17 22:47:01 +00:00
if err != nil {
return nil, err
}
revs := make(chan string, chanBufSize)
2017-05-24 18:54:32 +00:00
errs := make(chan error, 5) // may be multiple errors
2016-11-17 22:47:01 +00:00
go func() {
for scanner.Scan() {
sha := hex.EncodeToString(scanner.OID())
if name := scanner.Name(); len(name) > 0 {
opt.SetName(sha, name)
2016-11-17 22:47:01 +00:00
}
2017-05-24 18:54:32 +00:00
revs <- sha
2016-11-17 22:47:01 +00:00
}
2017-05-24 18:54:32 +00:00
if err = scanner.Err(); err != nil {
errs <- err
2016-11-17 22:47:01 +00:00
}
2017-05-24 18:54:32 +00:00
if err = scanner.Close(); err != nil {
errs <- err
}
2016-11-17 22:47:01 +00:00
close(revs)
2017-05-24 18:54:32 +00:00
close(errs)
2016-11-17 22:47:01 +00:00
}()
2017-05-24 18:54:32 +00:00
return NewStringChannelWrapper(revs, errs), nil
2016-11-17 22:47:01 +00:00
}