2016-11-03 17:16:01 -05:00
|
|
|
// Copyright 2015 The Gogs Authors. All rights reserved.
|
2019-04-19 07:17:27 -05:00
|
|
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
2022-11-27 13:20:29 -05:00
|
|
|
// SPDX-License-Identifier: MIT
|
2016-11-03 17:16:01 -05:00
|
|
|
|
|
|
|
package git
|
|
|
|
|
|
|
|
import (
|
2021-07-20 08:16:20 -05:00
|
|
|
"bytes"
|
2024-08-12 10:16:55 -05:00
|
|
|
"io"
|
2016-11-03 17:16:01 -05:00
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
2024-08-12 10:16:55 -05:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2019-04-19 07:17:27 -05:00
|
|
|
// NewTree create a new tree according the repository and tree id
|
2023-12-13 16:02:00 -05:00
|
|
|
func NewTree(repo *Repository, id ObjectID) *Tree {
|
2016-11-03 17:16:01 -05:00
|
|
|
return &Tree{
|
|
|
|
ID: id,
|
|
|
|
repo: repo,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-12 10:16:55 -05:00
|
|
|
// 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(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"})
|
|
|
|
}
|
|
|
|
|
2016-12-22 04:30:52 -05:00
|
|
|
// SubTree get a sub tree by the sub dir path
|
2016-11-03 17:16:01 -05:00
|
|
|
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
|
|
|
|
}
|
2021-07-20 08:16:20 -05:00
|
|
|
|
|
|
|
// LsTree checks if the given filenames are in the tree
|
|
|
|
func (repo *Repository) LsTree(ref string, filenames ...string) ([]string, error) {
|
2022-10-23 09:44:45 -05:00
|
|
|
cmd := NewCommand(repo.Ctx, "ls-tree", "-z", "--name-only").
|
|
|
|
AddDashesAndList(append([]string{ref}, filenames...)...)
|
|
|
|
|
2022-03-31 21:55:30 -05:00
|
|
|
res, _, err := cmd.RunStdBytes(&RunOpts{Dir: repo.Path})
|
2021-07-20 08:16:20 -05:00
|
|
|
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
|
|
|
|
}
|