2016-11-17 22:47:01 +00:00
|
|
|
package lfs
|
|
|
|
|
|
|
|
import (
|
2017-05-24 18:54:32 +00:00
|
|
|
"encoding/hex"
|
2016-11-17 22:47:01 +00:00
|
|
|
"regexp"
|
2017-05-24 18:54:32 +00:00
|
|
|
|
|
|
|
"github.com/git-lfs/git-lfs/git"
|
2016-11-17 22:47:01 +00:00
|
|
|
)
|
|
|
|
|
2016-11-18 18:51:15 +00:00
|
|
|
var z40 = regexp.MustCompile(`\^?0{40}`)
|
|
|
|
|
2017-02-16 23:52:40 +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) {}
|
|
|
|
|
2018-06-25 23:21:55 +00:00
|
|
|
// scanRefsToChan scans through all commits reachable by refs contained in
|
|
|
|
// "include" and not reachable by any refs included in "excluded" and returns
|
|
|
|
// a channel of WrappedPointer objects for all Git LFS pointers 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
|
2018-06-25 23:21:55 +00:00
|
|
|
func scanRefsToChan(scanner *GitScanner, pointerCb GitScannerFoundPointer, include, exclude []string, opt *ScanRefsOptions) error {
|
2016-11-17 22:47:01 +00:00
|
|
|
if opt == nil {
|
|
|
|
panic("no scan ref options")
|
|
|
|
}
|
|
|
|
|
2018-06-25 23:21:55 +00:00
|
|
|
revs, err := revListShas(include, exclude, opt)
|
2016-11-17 22:47:01 +00:00
|
|
|
if err != nil {
|
2016-11-29 20:04:05 +00:00
|
|
|
return err
|
2016-11-17 22:47:01 +00:00
|
|
|
}
|
|
|
|
|
2017-02-16 23:52:40 +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 {
|
2016-11-29 20:04:05 +00:00
|
|
|
return err
|
2016-11-17 22:47:01 +00:00
|
|
|
}
|
|
|
|
|
2017-02-16 23:52:40 +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)
|
2016-11-17 22:47:01 +00:00
|
|
|
if err != nil {
|
2016-11-29 20:04:05 +00:00
|
|
|
return err
|
2016-11-17 22:47:01 +00:00
|
|
|
}
|
|
|
|
|
2016-11-29 20:04:05 +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
|
|
|
}
|
2018-02-01 02:16:28 +00:00
|
|
|
|
|
|
|
if scanner.Filter.Allows(p.Name) {
|
|
|
|
pointerCb(p, nil)
|
|
|
|
}
|
2017-02-16 23:52:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for lockableName := range checkLockableCh {
|
2018-02-01 02:16:28 +00:00
|
|
|
if scanner.Filter.Allows(lockableName) {
|
|
|
|
lockableCb(lockableName)
|
|
|
|
}
|
2016-11-29 20:04:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if err := pointers.Wait(); err != nil {
|
2017-02-16 23:52:40 +00:00
|
|
|
pointerCb(nil, err)
|
2016-11-29 20:04:05 +00:00
|
|
|
}
|
2016-11-17 22:47:01 +00:00
|
|
|
|
2016-11-29 20:04:05 +00:00
|
|
|
return nil
|
2016-11-17 22:47:01 +00:00
|
|
|
}
|
|
|
|
|
2018-06-25 23:21:55 +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, opt *ScanRefsOptions) error {
|
|
|
|
return scanRefsToChan(scanner, pointerCb, []string{refLeft, refRight}, nil, opt)
|
|
|
|
}
|
|
|
|
|
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.
|
2017-06-08 17:22:30 +00:00
|
|
|
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,
|
|
|
|
})
|
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() {
|
2017-05-24 19:52:01 +00:00
|
|
|
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
|
|
|
}
|