File: gitscanner_catfilebatchcheck.go

package info (click to toggle)
git-lfs 3.6.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 4,808 kB
  • sloc: sh: 21,256; makefile: 507; ruby: 417
file content (119 lines) | stat: -rw-r--r-- 2,795 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
package lfs

import (
	"bufio"
	"io"
	"strconv"
	"strings"

	"github.com/git-lfs/git-lfs/v3/errors"
	"github.com/git-lfs/git-lfs/v3/git"
	"github.com/git-lfs/git-lfs/v3/tr"
)

// runCatFileBatchCheck uses 'git cat-file --batch-check' to get the type and
// size of a git object. Any object that isn't of type blob and under the
// blobSizeCutoff will be ignored, unless it's a locked file. revs is a channel
// over which strings containing git sha1s will be sent. It returns a channel
// from which sha1 strings can be read.
func runCatFileBatchCheck(smallRevCh chan string, lockableCh chan string, lockableSet *lockableNameSet, revs *StringChannelWrapper, errCh chan error) error {
	cmd, err := git.CatFile()
	if err != nil {
		return err
	}

	go func() {
		scanner := &catFileBatchCheckScanner{s: bufio.NewScanner(cmd.Stdout), limit: blobSizeCutoff}
		for r := range revs.Results {
			cmd.Stdin.Write([]byte(r + "\n"))
			hasNext := scanner.Scan()
			if err := scanner.Err(); err != nil {
				errCh <- err
			} else if b := scanner.LFSBlobOID(); len(b) > 0 {
				smallRevCh <- b
			} else if b := scanner.GitBlobOID(); len(b) > 0 {
				if name, ok := lockableSet.Check(b); ok {
					lockableCh <- name
				}
			}

			if !hasNext {
				break
			}
		}

		if err := revs.Wait(); err != nil {
			errCh <- err
		}
		cmd.Stdin.Close()

		stderr, _ := io.ReadAll(cmd.Stderr)
		err := cmd.Wait()
		if err != nil {
			errCh <- errors.New(tr.Tr.Get("error in `git cat-file --batch-check`: %v %v", err, string(stderr)))
		}
		close(smallRevCh)
		close(errCh)
		close(lockableCh)
	}()

	return nil
}

type catFileBatchCheckScanner struct {
	s          *bufio.Scanner
	limit      int
	lfsBlobOID string
	gitBlobOID string
}

func (s *catFileBatchCheckScanner) LFSBlobOID() string {
	return s.lfsBlobOID
}

func (s *catFileBatchCheckScanner) GitBlobOID() string {
	return s.gitBlobOID
}

func (s *catFileBatchCheckScanner) Err() error {
	return s.s.Err()
}

func (s *catFileBatchCheckScanner) Scan() bool {
	lfsBlobSha, gitBlobSha, hasNext := s.next()
	s.lfsBlobOID = lfsBlobSha
	s.gitBlobOID = gitBlobSha
	return hasNext
}

func (s *catFileBatchCheckScanner) next() (string, string, bool) {
	hasNext := s.s.Scan()
	line := s.s.Text()
	lineLen := len(line)

	oidLen := strings.IndexByte(line, ' ')

	// Format is:
	// <hash> <type> <size>
	// type is at a fixed spot, if we see that it's "blob", we can avoid
	// splitting the line just to get the size.
	if oidLen == -1 || lineLen < oidLen+6 {
		return "", "", hasNext
	}

	if line[oidLen+1:oidLen+5] != "blob" {
		return "", "", hasNext
	}

	size, err := strconv.Atoi(line[oidLen+6 : lineLen])
	if err != nil {
		return "", "", hasNext
	}

	blobSha := line[0:oidLen]
	if size >= s.limit {
		return "", blobSha, hasNext
	}

	return blobSha, "", hasNext
}