0
0
mirror of https://github.com/go-gitea/gitea.git synced 2025-11-10 13:31:01 -05:00

Refactor ls-tree and git path related problems (#35858)

Fix #35852, the root problem is that the "name" field is heavily abused
(since #6816, and no way to get a clear fix)

There are still a lot of legacy problems in old code.

Co-authored-by: Giteabot <teabot@gitea.io>
This commit is contained in:
wxiaoguang
2025-11-06 01:48:38 +08:00
committed by GitHub
parent d0ca2f6bc3
commit 525265c1a8
24 changed files with 193 additions and 450 deletions

View File

@@ -41,8 +41,8 @@ func naturalSortAdvance(str string, pos int) (end int, isNumber bool) {
return end, isNumber
}
// NaturalSortLess compares two strings so that they could be sorted in natural order
func NaturalSortLess(s1, s2 string) bool {
// NaturalSortCompare compares two strings so that they could be sorted in natural order
func NaturalSortCompare(s1, s2 string) int {
// There is a bug in Golang's collate package: https://github.com/golang/go/issues/67997
// text/collate: CompareString(collate.Numeric) returns wrong result for "0.0" vs "1.0" #67997
// So we need to handle the number parts by ourselves
@@ -55,16 +55,16 @@ func NaturalSortLess(s1, s2 string) bool {
if isNum1 && isNum2 {
if part1 != part2 {
if len(part1) != len(part2) {
return len(part1) < len(part2)
return len(part1) - len(part2)
}
return part1 < part2
return c.CompareString(part1, part2)
}
} else {
if cmp := c.CompareString(part1, part2); cmp != 0 {
return cmp < 0
return cmp
}
}
pos1, pos2 = end1, end2
}
return len(s1) < len(s2)
return len(s1) - len(s2)
}

View File

@@ -11,12 +11,10 @@ import (
func TestNaturalSortLess(t *testing.T) {
testLess := func(s1, s2 string) {
assert.True(t, NaturalSortLess(s1, s2), "s1<s2 should be true: s1=%q, s2=%q", s1, s2)
assert.False(t, NaturalSortLess(s2, s1), "s2<s1 should be false: s1=%q, s2=%q", s1, s2)
assert.Negative(t, NaturalSortCompare(s1, s2), "s1<s2 should be true: s1=%q, s2=%q", s1, s2)
}
testEqual := func(s1, s2 string) {
assert.False(t, NaturalSortLess(s1, s2), "s1<s2 should be false: s1=%q, s2=%q", s1, s2)
assert.False(t, NaturalSortLess(s2, s1), "s2<s1 should be false: s1=%q, s2=%q", s1, s2)
assert.Zero(t, NaturalSortCompare(s1, s2), "s1<s2 should be false: s1=%q, s2=%q", s1, s2)
}
testEqual("", "")

View File

@@ -173,7 +173,6 @@ func BenchmarkEntries_GetCommitsInfo(b *testing.B) {
} else if entries, err = commit.Tree.ListEntries(); err != nil {
b.Fatal(err)
}
entries.Sort()
b.ResetTimer()
b.Run(benchmark.name, func(b *testing.B) {
for b.Loop() {

View File

@@ -7,6 +7,7 @@ package git
import (
"context"
"fmt"
"io"
"code.gitea.io/gitea/modules/log"
@@ -30,7 +31,11 @@ func GetNote(ctx context.Context, repo *Repository, commitID string, note *Note)
remainingCommitID := commitID
path := ""
currentTree := notes.Tree.gogitTree
currentTree, err := notes.Tree.gogitTreeObject()
if err != nil {
return fmt.Errorf("unable to get tree object for notes commit %q: %w", notes.ID.String(), err)
}
log.Trace("Found tree with ID %q while searching for git note corresponding to the commit %q", currentTree.Entries[0].Name, commitID)
var file *object.File
for len(remainingCommitID) > 2 {

View File

@@ -1,96 +0,0 @@
// Copyright 2018 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
//go:build gogit
package git
import (
"bytes"
"fmt"
"strconv"
"strings"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/filemode"
"github.com/go-git/go-git/v5/plumbing/hash"
"github.com/go-git/go-git/v5/plumbing/object"
)
// ParseTreeEntries parses the output of a `git ls-tree -l` command.
func ParseTreeEntries(data []byte) ([]*TreeEntry, error) {
return parseTreeEntries(data, nil)
}
func parseTreeEntries(data []byte, ptree *Tree) ([]*TreeEntry, error) {
entries := make([]*TreeEntry, 0, 10)
for pos := 0; pos < len(data); {
// expect line to be of the form "<mode> <type> <sha> <space-padded-size>\t<filename>"
entry := new(TreeEntry)
entry.gogitTreeEntry = &object.TreeEntry{}
entry.ptree = ptree
if pos+6 > len(data) {
return nil, fmt.Errorf("Invalid ls-tree output: %s", string(data))
}
switch string(data[pos : pos+6]) {
case "100644":
entry.gogitTreeEntry.Mode = filemode.Regular
pos += 12 // skip over "100644 blob "
case "100755":
entry.gogitTreeEntry.Mode = filemode.Executable
pos += 12 // skip over "100755 blob "
case "120000":
entry.gogitTreeEntry.Mode = filemode.Symlink
pos += 12 // skip over "120000 blob "
case "160000":
entry.gogitTreeEntry.Mode = filemode.Submodule
pos += 14 // skip over "160000 object "
case "040000":
entry.gogitTreeEntry.Mode = filemode.Dir
pos += 12 // skip over "040000 tree "
default:
return nil, fmt.Errorf("unknown type: %v", string(data[pos:pos+6]))
}
// in hex format, not byte format ....
if pos+hash.Size*2 > len(data) {
return nil, fmt.Errorf("Invalid ls-tree output: %s", string(data))
}
var err error
entry.ID, err = NewIDFromString(string(data[pos : pos+hash.Size*2]))
if err != nil {
return nil, fmt.Errorf("invalid ls-tree output: %w", err)
}
entry.gogitTreeEntry.Hash = plumbing.Hash(entry.ID.RawValue())
pos += 41 // skip over sha and trailing space
end := pos + bytes.IndexByte(data[pos:], '\t')
if end < pos {
return nil, fmt.Errorf("Invalid ls-tree -l output: %s", string(data))
}
entry.size, _ = strconv.ParseInt(strings.TrimSpace(string(data[pos:end])), 10, 64)
entry.sized = true
pos = end + 1
end = pos + bytes.IndexByte(data[pos:], '\n')
if end < pos {
return nil, fmt.Errorf("Invalid ls-tree output: %s", string(data))
}
// In case entry name is surrounded by double quotes(it happens only in git-shell).
if data[pos] == '"' {
var err error
entry.gogitTreeEntry.Name, err = strconv.Unquote(string(data[pos:end]))
if err != nil {
return nil, fmt.Errorf("Invalid ls-tree output: %w", err)
}
} else {
entry.gogitTreeEntry.Name = string(data[pos:end])
}
pos = end + 1
entries = append(entries, entry)
}
return entries, nil
}

View File

@@ -1,78 +0,0 @@
// Copyright 2018 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
//go:build gogit
package git
import (
"fmt"
"testing"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/filemode"
"github.com/go-git/go-git/v5/plumbing/object"
"github.com/stretchr/testify/assert"
)
func TestParseTreeEntries(t *testing.T) {
testCases := []struct {
Input string
Expected []*TreeEntry
}{
{
Input: "",
Expected: []*TreeEntry{},
},
{
Input: "100644 blob 61ab7345a1a3bbc590068ccae37b8515cfc5843c 1022\texample/file2.txt\n",
Expected: []*TreeEntry{
{
ID: MustIDFromString("61ab7345a1a3bbc590068ccae37b8515cfc5843c"),
gogitTreeEntry: &object.TreeEntry{
Hash: plumbing.Hash(MustIDFromString("61ab7345a1a3bbc590068ccae37b8515cfc5843c").RawValue()),
Name: "example/file2.txt",
Mode: filemode.Regular,
},
size: 1022,
sized: true,
},
},
},
{
Input: "120000 blob 61ab7345a1a3bbc590068ccae37b8515cfc5843c 234131\t\"example/\\n.txt\"\n" +
"040000 tree 1d01fb729fb0db5881daaa6030f9f2d3cd3d5ae8 -\texample\n",
Expected: []*TreeEntry{
{
ID: MustIDFromString("61ab7345a1a3bbc590068ccae37b8515cfc5843c"),
gogitTreeEntry: &object.TreeEntry{
Hash: plumbing.Hash(MustIDFromString("61ab7345a1a3bbc590068ccae37b8515cfc5843c").RawValue()),
Name: "example/\n.txt",
Mode: filemode.Symlink,
},
size: 234131,
sized: true,
},
{
ID: MustIDFromString("1d01fb729fb0db5881daaa6030f9f2d3cd3d5ae8"),
sized: true,
gogitTreeEntry: &object.TreeEntry{
Hash: plumbing.Hash(MustIDFromString("1d01fb729fb0db5881daaa6030f9f2d3cd3d5ae8").RawValue()),
Name: "example",
Mode: filemode.Dir,
},
},
},
},
}
for _, testCase := range testCases {
entries, err := ParseTreeEntries([]byte(testCase.Input))
assert.NoError(t, err)
if len(entries) > 1 {
fmt.Println(testCase.Expected[0].ID)
fmt.Println(entries[0].ID)
}
assert.EqualValues(t, testCase.Expected, entries)
}
}

View File

@@ -1,8 +1,6 @@
// Copyright 2018 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
//go:build !gogit
package git
import (

View File

@@ -1,8 +1,6 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
//go:build !gogit
package git
import (

View File

@@ -107,7 +107,7 @@ func (repo *Repository) getCommit(id ObjectID) (*Commit, error) {
}
commit.Tree.ID = ParseGogitHash(tree.Hash)
commit.Tree.gogitTree = tree
commit.Tree.resolvedGogitTreeObject = tree
return commit, nil
}

View File

@@ -26,7 +26,7 @@ func (repo *Repository) getTree(id ObjectID) (*Tree, error) {
}
tree := NewTree(repo, id)
tree.gogitTree = gogitTree
tree.resolvedGogitTreeObject = gogitTree
return tree, nil
}

View File

@@ -11,11 +11,21 @@ import (
"code.gitea.io/gitea/modules/git/gitcmd"
)
type TreeCommon struct {
ID ObjectID
ResolvedID ObjectID
repo *Repository
ptree *Tree // parent tree
}
// NewTree create a new tree according the repository and tree id
func NewTree(repo *Repository, id ObjectID) *Tree {
return &Tree{
TreeCommon: TreeCommon{
ID: id,
repo: repo,
},
}
}

View File

@@ -11,8 +11,6 @@ import (
"strings"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/filemode"
"github.com/go-git/go-git/v5/plumbing/object"
)
// GetTreeEntryByPath get the tree entries according the sub dir
@@ -20,13 +18,9 @@ func (t *Tree) GetTreeEntryByPath(relpath string) (*TreeEntry, error) {
if len(relpath) == 0 {
return &TreeEntry{
ID: t.ID,
// Type: ObjectTree,
ptree: t,
gogitTreeEntry: &object.TreeEntry{
Name: "",
Mode: filemode.Dir,
Hash: plumbing.Hash(t.ID.RawValue()),
},
name: "",
entryMode: EntryModeTree,
}, nil
}

View File

@@ -6,12 +6,60 @@ package git
import (
"path"
"sort"
"slices"
"strings"
"code.gitea.io/gitea/modules/util"
)
// TreeEntry the leaf in the git tree
type TreeEntry struct {
ID ObjectID
name string
ptree *Tree
entryMode EntryMode
size int64
sized bool
}
// Name returns the name of the entry (base name)
func (te *TreeEntry) Name() string {
return te.name
}
// Mode returns the mode of the entry
func (te *TreeEntry) Mode() EntryMode {
return te.entryMode
}
// IsSubModule if the entry is a submodule
func (te *TreeEntry) IsSubModule() bool {
return te.entryMode.IsSubModule()
}
// IsDir if the entry is a sub dir
func (te *TreeEntry) IsDir() bool {
return te.entryMode.IsDir()
}
// IsLink if the entry is a symlink
func (te *TreeEntry) IsLink() bool {
return te.entryMode.IsLink()
}
// IsRegular if the entry is a regular file
func (te *TreeEntry) IsRegular() bool {
return te.entryMode.IsRegular()
}
// IsExecutable if the entry is an executable file (not necessarily binary)
func (te *TreeEntry) IsExecutable() bool {
return te.entryMode.IsExecutable()
}
// Type returns the type of the entry (commit, tree, blob)
func (te *TreeEntry) Type() string {
switch te.Mode() {
@@ -109,49 +157,16 @@ func (te *TreeEntry) GetSubJumpablePathName() string {
// Entries a list of entry
type Entries []*TreeEntry
type customSortableEntries struct {
Comparer func(s1, s2 string) bool
Entries
}
var sorter = []func(t1, t2 *TreeEntry, cmp func(s1, s2 string) bool) bool{
func(t1, t2 *TreeEntry, cmp func(s1, s2 string) bool) bool {
return (t1.IsDir() || t1.IsSubModule()) && !t2.IsDir() && !t2.IsSubModule()
},
func(t1, t2 *TreeEntry, cmp func(s1, s2 string) bool) bool {
return cmp(t1.Name(), t2.Name())
},
}
func (ctes customSortableEntries) Len() int { return len(ctes.Entries) }
func (ctes customSortableEntries) Swap(i, j int) {
ctes.Entries[i], ctes.Entries[j] = ctes.Entries[j], ctes.Entries[i]
}
func (ctes customSortableEntries) Less(i, j int) bool {
t1, t2 := ctes.Entries[i], ctes.Entries[j]
var k int
for k = 0; k < len(sorter)-1; k++ {
s := sorter[k]
switch {
case s(t1, t2, ctes.Comparer):
return true
case s(t2, t1, ctes.Comparer):
return false
}
}
return sorter[k](t1, t2, ctes.Comparer)
}
// Sort sort the list of entry
func (tes Entries) Sort() {
sort.Sort(customSortableEntries{func(s1, s2 string) bool {
return s1 < s2
}, tes})
}
// CustomSort customizable string comparing sort entry list
func (tes Entries) CustomSort(cmp func(s1, s2 string) bool) {
sort.Sort(customSortableEntries{cmp, tes})
func (tes Entries) CustomSort(cmp func(s1, s2 string) int) {
slices.SortFunc(tes, func(a, b *TreeEntry) int {
s1Dir, s2Dir := a.IsDir() || a.IsSubModule(), b.IsDir() || b.IsSubModule()
if s1Dir != s2Dir {
if s1Dir {
return -1
}
return 1
}
return cmp(a.Name(), b.Name())
})
}

View File

@@ -12,25 +12,21 @@ import (
"github.com/go-git/go-git/v5/plumbing/object"
)
// TreeEntry the leaf in the git tree
type TreeEntry struct {
ID ObjectID
gogitTreeEntry *object.TreeEntry
ptree *Tree
size int64
sized bool
// gogitFileModeToEntryMode converts go-git filemode to EntryMode
func gogitFileModeToEntryMode(mode filemode.FileMode) EntryMode {
return EntryMode(mode)
}
// Name returns the name of the entry
func (te *TreeEntry) Name() string {
return te.gogitTreeEntry.Name
func entryModeToGogitFileMode(mode EntryMode) filemode.FileMode {
return filemode.FileMode(mode)
}
// Mode returns the mode of the entry
func (te *TreeEntry) Mode() EntryMode {
return EntryMode(te.gogitTreeEntry.Mode)
func (te *TreeEntry) toGogitTreeEntry() *object.TreeEntry {
return &object.TreeEntry{
Name: te.name,
Mode: entryModeToGogitFileMode(te.entryMode),
Hash: plumbing.Hash(te.ID.RawValue()),
}
}
// Size returns the size of the entry
@@ -41,7 +37,11 @@ func (te *TreeEntry) Size() int64 {
return te.size
}
file, err := te.ptree.gogitTree.TreeEntryFile(te.gogitTreeEntry)
ptreeGogitTree, err := te.ptree.gogitTreeObject()
if err != nil {
return 0
}
file, err := ptreeGogitTree.TreeEntryFile(te.toGogitTreeEntry())
if err != nil {
return 0
}
@@ -51,40 +51,15 @@ func (te *TreeEntry) Size() int64 {
return te.size
}
// IsSubModule if the entry is a submodule
func (te *TreeEntry) IsSubModule() bool {
return te.gogitTreeEntry.Mode == filemode.Submodule
}
// IsDir if the entry is a sub dir
func (te *TreeEntry) IsDir() bool {
return te.gogitTreeEntry.Mode == filemode.Dir
}
// IsLink if the entry is a symlink
func (te *TreeEntry) IsLink() bool {
return te.gogitTreeEntry.Mode == filemode.Symlink
}
// IsRegular if the entry is a regular file
func (te *TreeEntry) IsRegular() bool {
return te.gogitTreeEntry.Mode == filemode.Regular
}
// IsExecutable if the entry is an executable file (not necessarily binary)
func (te *TreeEntry) IsExecutable() bool {
return te.gogitTreeEntry.Mode == filemode.Executable
}
// Blob returns the blob object the entry
func (te *TreeEntry) Blob() *Blob {
encodedObj, err := te.ptree.repo.gogitRepo.Storer.EncodedObject(plumbing.AnyObject, te.gogitTreeEntry.Hash)
encodedObj, err := te.ptree.repo.gogitRepo.Storer.EncodedObject(plumbing.AnyObject, te.toGogitTreeEntry().Hash)
if err != nil {
return nil
}
return &Blob{
ID: ParseGogitHash(te.gogitTreeEntry.Hash),
ID: te.ID,
gogitEncodedObj: encodedObj,
name: te.Name(),
}

View File

@@ -0,0 +1,27 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
//go:build gogit
package git
import (
"testing"
"github.com/go-git/go-git/v5/plumbing/filemode"
"github.com/stretchr/testify/assert"
)
func TestEntryGogit(t *testing.T) {
cases := map[EntryMode]filemode.FileMode{
EntryModeBlob: filemode.Regular,
EntryModeCommit: filemode.Submodule,
EntryModeExec: filemode.Executable,
EntryModeSymlink: filemode.Symlink,
EntryModeTree: filemode.Dir,
}
for emode, fmode := range cases {
assert.EqualValues(t, fmode, entryModeToGogitFileMode(emode))
assert.EqualValues(t, emode, gogitFileModeToEntryMode(fmode))
}
}

View File

@@ -7,27 +7,6 @@ package git
import "code.gitea.io/gitea/modules/log"
// TreeEntry the leaf in the git tree
type TreeEntry struct {
ID ObjectID
ptree *Tree
entryMode EntryMode
name string
size int64
sized bool
}
// Name returns the name of the entry (base name)
func (te *TreeEntry) Name() string {
return te.name
}
// Mode returns the mode of the entry
func (te *TreeEntry) Mode() EntryMode {
return te.entryMode
}
// Size returns the size of the entry
func (te *TreeEntry) Size() int64 {
if te.IsDir() {
@@ -57,31 +36,6 @@ func (te *TreeEntry) Size() int64 {
return te.size
}
// IsSubModule if the entry is a submodule
func (te *TreeEntry) IsSubModule() bool {
return te.entryMode.IsSubModule()
}
// IsDir if the entry is a sub dir
func (te *TreeEntry) IsDir() bool {
return te.entryMode.IsDir()
}
// IsLink if the entry is a symlink
func (te *TreeEntry) IsLink() bool {
return te.entryMode.IsLink()
}
// IsRegular if the entry is a regular file
func (te *TreeEntry) IsRegular() bool {
return te.entryMode.IsRegular()
}
// IsExecutable if the entry is an executable file (not necessarily binary)
func (te *TreeEntry) IsExecutable() bool {
return te.entryMode.IsExecutable()
}
// Blob returns the blob object the entry
func (te *TreeEntry) Blob() *Blob {
return &Blob{

View File

@@ -1,55 +1,29 @@
// Copyright 2017 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
//go:build gogit
package git
import (
"math/rand/v2"
"slices"
"strings"
"testing"
"github.com/go-git/go-git/v5/plumbing/filemode"
"github.com/go-git/go-git/v5/plumbing/object"
"github.com/stretchr/testify/assert"
)
func getTestEntries() Entries {
return Entries{
&TreeEntry{gogitTreeEntry: &object.TreeEntry{Name: "v1.0", Mode: filemode.Dir}},
&TreeEntry{gogitTreeEntry: &object.TreeEntry{Name: "v2.0", Mode: filemode.Dir}},
&TreeEntry{gogitTreeEntry: &object.TreeEntry{Name: "v2.1", Mode: filemode.Dir}},
&TreeEntry{gogitTreeEntry: &object.TreeEntry{Name: "v2.12", Mode: filemode.Dir}},
&TreeEntry{gogitTreeEntry: &object.TreeEntry{Name: "v2.2", Mode: filemode.Dir}},
&TreeEntry{gogitTreeEntry: &object.TreeEntry{Name: "v12.0", Mode: filemode.Dir}},
&TreeEntry{gogitTreeEntry: &object.TreeEntry{Name: "abc", Mode: filemode.Regular}},
&TreeEntry{gogitTreeEntry: &object.TreeEntry{Name: "bcd", Mode: filemode.Regular}},
}
}
func TestEntriesSort(t *testing.T) {
entries := getTestEntries()
entries.Sort()
assert.Equal(t, "v1.0", entries[0].Name())
assert.Equal(t, "v12.0", entries[1].Name())
assert.Equal(t, "v2.0", entries[2].Name())
assert.Equal(t, "v2.1", entries[3].Name())
assert.Equal(t, "v2.12", entries[4].Name())
assert.Equal(t, "v2.2", entries[5].Name())
assert.Equal(t, "abc", entries[6].Name())
assert.Equal(t, "bcd", entries[7].Name())
}
func TestEntriesCustomSort(t *testing.T) {
entries := getTestEntries()
entries.CustomSort(func(s1, s2 string) bool {
return s1 > s2
})
assert.Equal(t, "v2.2", entries[0].Name())
assert.Equal(t, "v2.12", entries[1].Name())
assert.Equal(t, "v2.1", entries[2].Name())
assert.Equal(t, "v2.0", entries[3].Name())
assert.Equal(t, "v12.0", entries[4].Name())
assert.Equal(t, "v1.0", entries[5].Name())
assert.Equal(t, "bcd", entries[6].Name())
assert.Equal(t, "abc", entries[7].Name())
entries := Entries{
&TreeEntry{name: "a-dir", entryMode: EntryModeTree},
&TreeEntry{name: "a-submodule", entryMode: EntryModeCommit},
&TreeEntry{name: "b-dir", entryMode: EntryModeTree},
&TreeEntry{name: "b-submodule", entryMode: EntryModeCommit},
&TreeEntry{name: "a-file", entryMode: EntryModeBlob},
&TreeEntry{name: "b-file", entryMode: EntryModeBlob},
}
expected := slices.Clone(entries)
rand.Shuffle(len(entries), func(i, j int) { entries[i], entries[j] = entries[j], entries[i] })
assert.NotEqual(t, expected, entries)
entries.CustomSort(strings.Compare)
assert.Equal(t, expected, entries)
}

View File

@@ -15,41 +15,34 @@ import (
// Tree represents a flat directory listing.
type Tree struct {
ID ObjectID
ResolvedID ObjectID
repo *Repository
TreeCommon
gogitTree *object.Tree
// parent tree
ptree *Tree
resolvedGogitTreeObject *object.Tree
}
func (t *Tree) loadTreeObject() error {
gogitTree, err := t.repo.gogitRepo.TreeObject(plumbing.Hash(t.ID.RawValue()))
if err != nil {
return err
}
t.gogitTree = gogitTree
return nil
}
// ListEntries returns all entries of current tree.
func (t *Tree) ListEntries() (Entries, error) {
if t.gogitTree == nil {
err := t.loadTreeObject()
func (t *Tree) gogitTreeObject() (_ *object.Tree, err error) {
if t.resolvedGogitTreeObject == nil {
t.resolvedGogitTreeObject, err = t.repo.gogitRepo.TreeObject(plumbing.Hash(t.ID.RawValue()))
if err != nil {
return nil, err
}
}
return t.resolvedGogitTreeObject, nil
}
entries := make([]*TreeEntry, len(t.gogitTree.Entries))
for i, entry := range t.gogitTree.Entries {
// ListEntries returns all entries of current tree.
func (t *Tree) ListEntries() (Entries, error) {
gogitTree, err := t.gogitTreeObject()
if err != nil {
return nil, err
}
entries := make([]*TreeEntry, len(gogitTree.Entries))
for i, gogitTreeEntry := range gogitTree.Entries {
entries[i] = &TreeEntry{
ID: ParseGogitHash(entry.Hash),
gogitTreeEntry: &t.gogitTree.Entries[i],
ID: ParseGogitHash(gogitTreeEntry.Hash),
ptree: t,
name: gogitTreeEntry.Name,
entryMode: gogitFileModeToEntryMode(gogitTreeEntry.Mode),
}
}
@@ -57,37 +50,28 @@ func (t *Tree) ListEntries() (Entries, error) {
}
// ListEntriesRecursiveWithSize returns all entries of current tree recursively including all subtrees
func (t *Tree) ListEntriesRecursiveWithSize() (Entries, error) {
if t.gogitTree == nil {
err := t.loadTreeObject()
func (t *Tree) ListEntriesRecursiveWithSize() (entries Entries, _ error) {
gogitTree, err := t.gogitTreeObject()
if err != nil {
return nil, err
}
}
var entries []*TreeEntry
seen := map[plumbing.Hash]bool{}
walker := object.NewTreeWalker(t.gogitTree, true, seen)
walker := object.NewTreeWalker(gogitTree, true, nil)
for {
_, entry, err := walker.Next()
fullName, gogitTreeEntry, err := walker.Next()
if err == io.EOF {
break
}
if err != nil {
} else if err != nil {
return nil, err
}
if seen[entry.Hash] {
continue
}
convertedEntry := &TreeEntry{
ID: ParseGogitHash(entry.Hash),
gogitTreeEntry: &entry,
ptree: t,
ID: ParseGogitHash(gogitTreeEntry.Hash),
name: fullName, // FIXME: the "name" field is abused, here it is a full path
ptree: t, // FIXME: this ptree is not right, fortunately it isn't really used
entryMode: gogitFileModeToEntryMode(gogitTreeEntry.Mode),
}
entries = append(entries, convertedEntry)
}
return entries, nil
}

View File

@@ -14,18 +14,10 @@ import (
// Tree represents a flat directory listing.
type Tree struct {
ID ObjectID
ResolvedID ObjectID
repo *Repository
// parent tree
ptree *Tree
TreeCommon
entries Entries
entriesParsed bool
entriesRecursive Entries
entriesRecursiveParsed bool
}
// ListEntries returns all entries of current tree.
@@ -94,10 +86,6 @@ func (t *Tree) ListEntries() (Entries, error) {
// 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 gitcmd.TrustedCmdArgs) (Entries, error) {
if t.entriesRecursiveParsed {
return t.entriesRecursive, nil
}
stdout, _, runErr := gitcmd.NewCommand("ls-tree", "-t", "-r").
AddArguments(extraArgs...).
AddDynamicArguments(t.ID.String()).
@@ -107,13 +95,9 @@ func (t *Tree) listEntriesRecursive(extraArgs gitcmd.TrustedCmdArgs) (Entries, e
return nil, runErr
}
var err error
t.entriesRecursive, err = parseTreeEntries(stdout, t)
if err == nil {
t.entriesRecursiveParsed = true
}
return t.entriesRecursive, err
// FIXME: the "name" field is abused, here it is a full path
// FIXME: this ptree is not right, fortunately it isn't really used
return parseTreeEntries(stdout, t)
}
// ListEntriesRecursiveFast returns all entries of current tree recursively including all subtrees, no size

View File

@@ -415,6 +415,8 @@ func Diff(ctx *context.Context) {
ctx.ServerError("PostProcessCommitMessage", err)
return
}
} else if !git.IsErrNotExist(err) {
log.Error("GetNote: %v", err)
}
pr, _ := issues_model.GetPullRequestByMergedCommit(ctx, ctx.Repo.Repository.ID, commitID)

View File

@@ -33,7 +33,7 @@ func TreeList(ctx *context.Context) {
ctx.ServerError("ListEntriesRecursiveFast", err)
return
}
entries.CustomSort(base.NaturalSortLess)
entries.CustomSort(base.NaturalSortCompare)
files := make([]string, 0, len(entries))
for _, entry := range entries {

View File

@@ -307,7 +307,7 @@ func renderDirectoryFiles(ctx *context.Context, timeout time.Duration) git.Entri
ctx.ServerError("ListEntries", err)
return nil
}
allEntries.CustomSort(base.NaturalSortLess)
allEntries.CustomSort(base.NaturalSortCompare)
commitInfoCtx := gocontext.Context(ctx)
if timeout > 0 {

View File

@@ -67,7 +67,7 @@ func findReadmeFileInEntries(ctx *context.Context, parentDir string, entries []*
for _, entry := range entries {
if i, ok := util.IsReadmeFileExtension(entry.Name(), exts...); ok {
fullPath := path.Join(parentDir, entry.Name())
if readmeFiles[i] == nil || base.NaturalSortLess(readmeFiles[i].Name(), entry.Blob().Name()) {
if readmeFiles[i] == nil || base.NaturalSortCompare(readmeFiles[i].Name(), entry.Blob().Name()) < 0 {
if entry.IsLink() {
res, err := git.EntryFollowLinks(ctx.Repo.Commit, fullPath, entry)
if err == nil && (res.TargetEntry.IsExecutable() || res.TargetEntry.IsRegular()) {

View File

@@ -567,7 +567,7 @@ func WikiPages(ctx *context.Context) {
ctx.ServerError("ListEntries", err)
return
}
allEntries.CustomSort(base.NaturalSortLess)
allEntries.CustomSort(base.NaturalSortCompare)
entries, _, err := allEntries.GetCommitsInfo(ctx, ctx.Repo.RepoLink, commit, treePath)
if err != nil {