17
"code.gitea.io/gitea/modules/log"
19
"github.com/djherbis/buffer"
20
"github.com/djherbis/nio/v3"
24
type WriteCloserError interface {
26
CloseWithError(err error) error
32
func EnsureValidGitRepository(ctx context.Context, repoPath string) error {
33
stderr := strings.Builder{}
34
err := NewCommand(ctx, "rev-parse").
35
SetDescription(fmt.Sprintf("%s rev-parse [repo_path: %s]", GitExecutable, repoPath)).
41
return ConcatenateError(err, (&stderr).String())
47
func CatFileBatchCheck(ctx context.Context, repoPath string) (WriteCloserError, *bufio.Reader, func()) {
48
batchStdinReader, batchStdinWriter := io.Pipe()
49
batchStdoutReader, batchStdoutWriter := io.Pipe()
50
ctx, ctxCancel := context.WithCancel(ctx)
51
closed := make(chan struct{})
54
_ = batchStdoutReader.Close()
55
_ = batchStdinWriter.Close()
65
_, filename, line, _ := runtime.Caller(2)
66
filename = strings.TrimPrefix(filename, callerPrefix)
69
stderr := strings.Builder{}
70
err := NewCommand(ctx, "cat-file", "--batch-check").
71
SetDescription(fmt.Sprintf("%s cat-file --batch-check [repo_path: %s] (%s:%d)", GitExecutable, repoPath, filename, line)).
74
Stdin: batchStdinReader,
75
Stdout: batchStdoutWriter,
78
UseContextTimeout: true,
81
_ = batchStdoutWriter.CloseWithError(ConcatenateError(err, (&stderr).String()))
82
_ = batchStdinReader.CloseWithError(ConcatenateError(err, (&stderr).String()))
84
_ = batchStdoutWriter.Close()
85
_ = batchStdinReader.Close()
91
batchReader := bufio.NewReader(batchStdoutReader)
93
return batchStdinWriter, batchReader, cancel
97
func CatFileBatch(ctx context.Context, repoPath string) (WriteCloserError, *bufio.Reader, func()) {
100
batchStdinReader, batchStdinWriter := io.Pipe()
101
batchStdoutReader, batchStdoutWriter := nio.Pipe(buffer.New(32 * 1024))
102
ctx, ctxCancel := context.WithCancel(ctx)
103
closed := make(chan struct{})
106
_ = batchStdinWriter.Close()
107
_ = batchStdoutReader.Close()
117
_, filename, line, _ := runtime.Caller(2)
118
filename = strings.TrimPrefix(filename, callerPrefix)
121
stderr := strings.Builder{}
122
err := NewCommand(ctx, "cat-file", "--batch").
123
SetDescription(fmt.Sprintf("%s cat-file --batch [repo_path: %s] (%s:%d)", GitExecutable, repoPath, filename, line)).
126
Stdin: batchStdinReader,
127
Stdout: batchStdoutWriter,
130
UseContextTimeout: true,
133
_ = batchStdoutWriter.CloseWithError(ConcatenateError(err, (&stderr).String()))
134
_ = batchStdinReader.CloseWithError(ConcatenateError(err, (&stderr).String()))
136
_ = batchStdoutWriter.Close()
137
_ = batchStdinReader.Close()
143
batchReader := bufio.NewReaderSize(batchStdoutReader, 32*1024)
145
return batchStdinWriter, batchReader, cancel
152
func ReadBatchLine(rd *bufio.Reader) (sha []byte, typ string, size int64, err error) {
153
typ, err = rd.ReadString('\n')
155
return sha, typ, size, err
158
typ, err = rd.ReadString('\n')
160
return sha, typ, size, err
163
idx := strings.IndexByte(typ, ' ')
165
log.Debug("missing space typ: %s", typ)
166
return sha, typ, size, ErrNotExist{ID: string(sha)}
168
sha = []byte(typ[:idx])
171
idx = strings.IndexByte(typ, ' ')
173
return sha, typ, size, ErrNotExist{ID: string(sha)}
176
sizeStr := typ[idx+1 : len(typ)-1]
179
size, err = strconv.ParseInt(sizeStr, 10, 64)
180
return sha, typ, size, err
184
func ReadTagObjectID(rd *bufio.Reader, size int64) (string, error) {
189
line, err := rd.ReadBytes('\n')
193
n += int64(len(line))
194
idx := bytes.Index(line, []byte{' '})
199
if string(line[:idx]) == "object" {
200
id = string(line[idx+1 : len(line)-1])
206
return id, DiscardFull(rd, size-n+1)
210
func ReadTreeID(rd *bufio.Reader, size int64) (string, error) {
215
line, err := rd.ReadBytes('\n')
219
n += int64(len(line))
220
idx := bytes.Index(line, []byte{' '})
225
if string(line[:idx]) == "tree" {
226
id = string(line[idx+1 : len(line)-1])
232
return id, DiscardFull(rd, size-n+1)
242
const hextable = "0123456789abcdef"
247
func BinToHex(objectFormat ObjectFormat, sha, out []byte) []byte {
248
for i := objectFormat.FullLength()/2 - 1; i >= 0; i-- {
250
vhi, vlo := v>>4, v&0x0f
251
shi, slo := hextable[vhi], hextable[vlo]
252
out[i*2], out[i*2+1] = shi, slo
265
func ParseTreeLine(objectFormat ObjectFormat, rd *bufio.Reader, modeBuf, fnameBuf, shaBuf []byte) (mode, fname, sha []byte, n int, err error) {
269
readBytes, err = rd.ReadSlice('\x00')
271
return mode, fname, sha, n, err
273
idx := bytes.IndexByte(readBytes, ' ')
275
log.Debug("missing space in readBytes ParseTreeLine: %s", readBytes)
276
return mode, fname, sha, n, &ErrNotExist{}
280
copy(modeBuf, readBytes[:idx])
281
if len(modeBuf) >= idx {
282
modeBuf = modeBuf[:idx]
284
modeBuf = append(modeBuf, readBytes[len(modeBuf):idx]...)
288
readBytes = readBytes[idx+1:]
291
copy(fnameBuf, readBytes)
292
if len(fnameBuf) > len(readBytes) {
293
fnameBuf = fnameBuf[:len(readBytes)]
295
fnameBuf = append(fnameBuf, readBytes[len(fnameBuf):]...)
297
for err == bufio.ErrBufferFull {
298
readBytes, err = rd.ReadSlice('\x00')
299
fnameBuf = append(fnameBuf, readBytes...)
303
return mode, fname, sha, n, err
305
fnameBuf = fnameBuf[:len(fnameBuf)-1]
310
len := objectFormat.FullLength() / 2
313
read, err = rd.Read(shaBuf[idx:len])
316
return mode, fname, sha, n, err
321
return mode, fname, sha, n, err
324
var callerPrefix string
327
_, filename, _, _ := runtime.Caller(0)
328
callerPrefix = strings.TrimSuffix(filename, "modules/git/batch_reader.go")
331
func DiscardFull(rd *bufio.Reader, discard int64) error {
332
if discard > math.MaxInt32 {
333
n, err := rd.Discard(math.MaxInt32)
340
n, err := rd.Discard(int(discard))