189 lines · 4.6 KB · Go
RawDownload
// 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 idfunc 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 slowerfunc (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 sizefunc (t *Tree) ListEntriesRecursiveFast() (Entries, error) {	return t.listEntriesRecursive(nil)} // ListEntriesRecursiveWithSize returns all entries of current tree recursively including all subtrees, with sizefunc (t *Tree) ListEntriesRecursiveWithSize() (Entries, error) {	return t.listEntriesRecursive(TrustedCmdArgs{"--long"})} // SubTree get a sub tree by the sub dir pathfunc (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 treefunc (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} // GetTreePathLatestCommitID returns the latest commit of a tree pathfunc (repo *Repository) GetTreePathLatestCommit(refName, treePath string) (*Commit, error) {	stdout, _, err := NewCommand(repo.Ctx, "rev-list", "-1").		AddDynamicArguments(refName).AddDashesAndList(treePath).		RunStdString(&RunOpts{Dir: repo.Path})	if err != nil {		return nil, err	}	return repo.GetCommit(strings.TrimSpace(stdout))}