mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-11-01 15:19:09 +01:00
0ca13c5eae
When opening a repository, it will call `ensureValidRepository` and also `CatFileBatch`. But sometimes these will not be used until repository closed. So it's a waste of CPU to invoke 3 times git command for every open repository. This PR removed all of these from `OpenRepository` but only kept checking whether the folder exists. When a batch is necessary, the necessary functions will be invoked. --- Conflict resolution: Because of the removal of go-git in (#4941) `_nogogit.go` files were either renamed or merged into the 'common' file. Git does handle the renames correctly, but for those that were merged has to be manually copied pasted over. The patch looks the same, 201 additions 90 deletions as the original patch. (cherry picked from commit c03baab678ba5b2e9d974aea147e660417f5d3f7)
178 lines
4.2 KiB
Go
178 lines
4.2 KiB
Go
// Copyright 2015 The Gogs Authors. All rights reserved.
|
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package git
|
|
|
|
import (
|
|
"bytes"
|
|
"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
|
|
}
|
|
|
|
// NewTree create a new tree according the repository and tree id
|
|
func NewTree(repo *Repository, id ObjectID) *Tree {
|
|
return &Tree{
|
|
ID: id,
|
|
repo: repo,
|
|
}
|
|
}
|
|
|
|
// 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, err := t.repo.CatFileBatch(t.repo.Ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
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(extraArgs TrustedCmdArgs) (Entries, error) {
|
|
if t.entriesRecursiveParsed {
|
|
return t.entriesRecursive, nil
|
|
}
|
|
|
|
stdout, _, runErr := NewCommand(t.repo.Ctx, "ls-tree", "-t", "-r").
|
|
AddArguments(extraArgs...).
|
|
AddDynamicArguments(t.ID.String()).
|
|
RunStdBytes(&RunOpts{Dir: t.repo.Path})
|
|
if runErr != nil {
|
|
return nil, runErr
|
|
}
|
|
|
|
var err error
|
|
t.entriesRecursive, err = parseTreeEntries(stdout, t)
|
|
if err == nil {
|
|
t.entriesRecursiveParsed = true
|
|
}
|
|
|
|
return t.entriesRecursive, err
|
|
}
|
|
|
|
// ListEntriesRecursiveFast returns all entries of current tree recursively including all subtrees, no size
|
|
func (t *Tree) ListEntriesRecursiveFast() (Entries, error) {
|
|
return t.listEntriesRecursive(nil)
|
|
}
|
|
|
|
// ListEntriesRecursiveWithSize returns all entries of current tree recursively including all subtrees, with size
|
|
func (t *Tree) ListEntriesRecursiveWithSize() (Entries, error) {
|
|
return t.listEntriesRecursive(TrustedCmdArgs{"--long"})
|
|
}
|
|
|
|
// SubTree get a sub tree by the sub dir path
|
|
func (t *Tree) SubTree(rpath string) (*Tree, error) {
|
|
if len(rpath) == 0 {
|
|
return t, nil
|
|
}
|
|
|
|
paths := strings.Split(rpath, "/")
|
|
var (
|
|
err error
|
|
g = t
|
|
p = t
|
|
te *TreeEntry
|
|
)
|
|
for _, name := range paths {
|
|
te, err = p.GetTreeEntryByPath(name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
g, err = t.repo.getTree(te.ID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
g.ptree = p
|
|
p = g
|
|
}
|
|
return g, nil
|
|
}
|
|
|
|
// LsTree checks if the given filenames are in the tree
|
|
func (repo *Repository) LsTree(ref string, filenames ...string) ([]string, error) {
|
|
cmd := NewCommand(repo.Ctx, "ls-tree", "-z", "--name-only").
|
|
AddDashesAndList(append([]string{ref}, filenames...)...)
|
|
|
|
res, _, err := cmd.RunStdBytes(&RunOpts{Dir: repo.Path})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
filelist := make([]string, 0, len(filenames))
|
|
for _, line := range bytes.Split(res, []byte{'\000'}) {
|
|
filelist = append(filelist, string(line))
|
|
}
|
|
|
|
return filelist, err
|
|
}
|