mirror of
https://github.com/go-gitea/gitea.git
synced 2024-09-01 14:56:30 +00:00
170 lines
4.2 KiB
Go
170 lines
4.2 KiB
Go
// Copyright 2020 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
//go:build !gogit
|
|
|
|
package git
|
|
|
|
import (
|
|
"bufio"
|
|
"context"
|
|
"io"
|
|
"strings"
|
|
)
|
|
|
|
// Tree represents a flat directory listing.
|
|
type Tree struct {
|
|
ID ObjectID
|
|
ResolvedID ObjectID
|
|
repo *Repository
|
|
|
|
// parent tree
|
|
ptree *Tree
|
|
|
|
entries Entries
|
|
entriesParsed bool
|
|
|
|
entriesRecursive Entries
|
|
entriesRecursiveParsed bool
|
|
}
|
|
|
|
// ListEntries returns all entries of current tree.
|
|
func (t *Tree) ListEntries() (Entries, error) {
|
|
if t.entriesParsed {
|
|
return t.entries, nil
|
|
}
|
|
|
|
if t.repo != nil {
|
|
wr, rd, cancel := t.repo.CatFileBatch(t.repo.Ctx)
|
|
defer cancel()
|
|
|
|
_, _ = wr.Write([]byte(t.ID.String() + "\n"))
|
|
_, typ, sz, err := ReadBatchLine(rd)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if typ == "commit" {
|
|
treeID, err := ReadTreeID(rd, sz)
|
|
if err != nil && err != io.EOF {
|
|
return nil, err
|
|
}
|
|
_, _ = wr.Write([]byte(treeID + "\n"))
|
|
_, typ, sz, err = ReadBatchLine(rd)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
if typ == "tree" {
|
|
t.entries, err = catBatchParseTreeEntries(t.ID.Type(), t, rd, sz)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
t.entriesParsed = true
|
|
return t.entries, nil
|
|
}
|
|
|
|
// Not a tree just use ls-tree instead
|
|
if err := DiscardFull(rd, sz+1); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
stdout, _, runErr := NewCommand(t.repo.Ctx, "ls-tree", "-l").AddDynamicArguments(t.ID.String()).RunStdBytes(&RunOpts{Dir: t.repo.Path})
|
|
if runErr != nil {
|
|
if strings.Contains(runErr.Error(), "fatal: Not a valid object name") || strings.Contains(runErr.Error(), "fatal: not a tree object") {
|
|
return nil, ErrNotExist{
|
|
ID: t.ID.String(),
|
|
}
|
|
}
|
|
return nil, runErr
|
|
}
|
|
|
|
var err error
|
|
t.entries, err = parseTreeEntries(stdout, t)
|
|
if err == nil {
|
|
t.entriesParsed = true
|
|
}
|
|
|
|
return t.entries, err
|
|
}
|
|
|
|
// listEntriesRecursive returns all entries of current tree recursively including all subtrees
|
|
// extraArgs could be "-l" to get the size, which is slower
|
|
func (t *Tree) listEntriesRecursive(ctx context.Context, extraArgs TrustedCmdArgs) (Entries, error) {
|
|
if t.entriesRecursiveParsed {
|
|
return t.entriesRecursive, nil
|
|
}
|
|
|
|
t.entriesRecursive = make([]*TreeEntry, 0)
|
|
err := t.iterateEntriesRecursive(func(entry *TreeEntry) error {
|
|
select {
|
|
case <-ctx.Done():
|
|
return ctx.Err()
|
|
default:
|
|
}
|
|
t.entriesRecursive = append(t.entriesRecursive, entry)
|
|
return nil
|
|
}, extraArgs)
|
|
return t.entriesRecursive, err
|
|
}
|
|
|
|
// ListEntriesRecursiveFast returns all entries of current tree recursively including all subtrees, no size
|
|
func (t *Tree) ListEntriesRecursiveFast(ctx context.Context) (Entries, error) {
|
|
return t.listEntriesRecursive(ctx, nil)
|
|
}
|
|
|
|
// ListEntriesRecursiveWithSize returns all entries of current tree recursively including all subtrees, with size
|
|
func (t *Tree) ListEntriesRecursiveWithSize(ctx context.Context) (Entries, error) {
|
|
return t.listEntriesRecursive(ctx, TrustedCmdArgs{"--long"})
|
|
}
|
|
|
|
// iterateEntriesRecursive returns iterate entries of current tree recursively including all subtrees
|
|
// extraArgs could be "-l" to get the size, which is slower
|
|
func (t *Tree) iterateEntriesRecursive(f func(entry *TreeEntry) error, extraArgs TrustedCmdArgs) error {
|
|
if t.entriesRecursiveParsed {
|
|
return nil
|
|
}
|
|
|
|
reader, writer := io.Pipe()
|
|
done := make(chan error)
|
|
|
|
go func(done chan error, writer *io.PipeWriter, reader *io.PipeReader) {
|
|
runErr := NewCommand(t.repo.Ctx, "ls-tree", "-t", "-r").
|
|
AddArguments(extraArgs...).
|
|
AddDynamicArguments(t.ID.String()).
|
|
Run(&RunOpts{
|
|
Dir: t.repo.Path,
|
|
Stdout: writer,
|
|
})
|
|
|
|
_ = writer.Close()
|
|
_ = reader.Close()
|
|
|
|
done <- runErr
|
|
}(done, writer, reader)
|
|
|
|
scanner := bufio.NewScanner(reader)
|
|
for scanner.Scan() {
|
|
if err := scanner.Err(); err != nil {
|
|
return err
|
|
}
|
|
data := scanner.Bytes()
|
|
if err := iterateTreeEntries(data, t, func(entry *TreeEntry) error {
|
|
select {
|
|
case runErr := <-done:
|
|
return runErr
|
|
default:
|
|
return f(entry)
|
|
}
|
|
}); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
t.entriesRecursiveParsed = true
|
|
return nil
|
|
}
|
|
|
|
func (t *Tree) IterateEntriesWithSize(f func(*TreeEntry) error) error {
|
|
return t.iterateEntriesRecursive(f, TrustedCmdArgs{"--long"})
|
|
}
|