mirror of
https://github.com/go-gitea/gitea
synced 2025-11-10 16:28:13 +00: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:
@@ -41,8 +41,8 @@ func naturalSortAdvance(str string, pos int) (end int, isNumber bool) {
|
|||||||
return end, isNumber
|
return end, isNumber
|
||||||
}
|
}
|
||||||
|
|
||||||
// NaturalSortLess compares two strings so that they could be sorted in natural order
|
// NaturalSortCompare compares two strings so that they could be sorted in natural order
|
||||||
func NaturalSortLess(s1, s2 string) bool {
|
func NaturalSortCompare(s1, s2 string) int {
|
||||||
// There is a bug in Golang's collate package: https://github.com/golang/go/issues/67997
|
// 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
|
// 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
|
// So we need to handle the number parts by ourselves
|
||||||
@@ -55,16 +55,16 @@ func NaturalSortLess(s1, s2 string) bool {
|
|||||||
if isNum1 && isNum2 {
|
if isNum1 && isNum2 {
|
||||||
if part1 != part2 {
|
if part1 != part2 {
|
||||||
if len(part1) != len(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 {
|
} else {
|
||||||
if cmp := c.CompareString(part1, part2); cmp != 0 {
|
if cmp := c.CompareString(part1, part2); cmp != 0 {
|
||||||
return cmp < 0
|
return cmp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pos1, pos2 = end1, end2
|
pos1, pos2 = end1, end2
|
||||||
}
|
}
|
||||||
return len(s1) < len(s2)
|
return len(s1) - len(s2)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,12 +11,10 @@ import (
|
|||||||
|
|
||||||
func TestNaturalSortLess(t *testing.T) {
|
func TestNaturalSortLess(t *testing.T) {
|
||||||
testLess := func(s1, s2 string) {
|
testLess := func(s1, s2 string) {
|
||||||
assert.True(t, NaturalSortLess(s1, s2), "s1<s2 should be true: s1=%q, s2=%q", s1, s2)
|
assert.Negative(t, NaturalSortCompare(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)
|
|
||||||
}
|
}
|
||||||
testEqual := func(s1, s2 string) {
|
testEqual := func(s1, s2 string) {
|
||||||
assert.False(t, NaturalSortLess(s1, s2), "s1<s2 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)
|
||||||
assert.False(t, NaturalSortLess(s2, s1), "s2<s1 should be false: s1=%q, s2=%q", s1, s2)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
testEqual("", "")
|
testEqual("", "")
|
||||||
|
|||||||
@@ -173,7 +173,6 @@ func BenchmarkEntries_GetCommitsInfo(b *testing.B) {
|
|||||||
} else if entries, err = commit.Tree.ListEntries(); err != nil {
|
} else if entries, err = commit.Tree.ListEntries(); err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
entries.Sort()
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.Run(benchmark.name, func(b *testing.B) {
|
b.Run(benchmark.name, func(b *testing.B) {
|
||||||
for b.Loop() {
|
for b.Loop() {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ package git
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
@@ -30,7 +31,11 @@ func GetNote(ctx context.Context, repo *Repository, commitID string, note *Note)
|
|||||||
|
|
||||||
remainingCommitID := commitID
|
remainingCommitID := commitID
|
||||||
path := ""
|
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)
|
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
|
var file *object.File
|
||||||
for len(remainingCommitID) > 2 {
|
for len(remainingCommitID) > 2 {
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
// Copyright 2018 The Gitea Authors. All rights reserved.
|
// Copyright 2018 The Gitea Authors. All rights reserved.
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
//go:build !gogit
|
|
||||||
|
|
||||||
package git
|
package git
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
// Copyright 2021 The Gitea Authors. All rights reserved.
|
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
//go:build !gogit
|
|
||||||
|
|
||||||
package git
|
package git
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -107,7 +107,7 @@ func (repo *Repository) getCommit(id ObjectID) (*Commit, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
commit.Tree.ID = ParseGogitHash(tree.Hash)
|
commit.Tree.ID = ParseGogitHash(tree.Hash)
|
||||||
commit.Tree.gogitTree = tree
|
commit.Tree.resolvedGogitTreeObject = tree
|
||||||
|
|
||||||
return commit, nil
|
return commit, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ func (repo *Repository) getTree(id ObjectID) (*Tree, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tree := NewTree(repo, id)
|
tree := NewTree(repo, id)
|
||||||
tree.gogitTree = gogitTree
|
tree.resolvedGogitTreeObject = gogitTree
|
||||||
return tree, nil
|
return tree, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,11 +11,21 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/git/gitcmd"
|
"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
|
// NewTree create a new tree according the repository and tree id
|
||||||
func NewTree(repo *Repository, id ObjectID) *Tree {
|
func NewTree(repo *Repository, id ObjectID) *Tree {
|
||||||
return &Tree{
|
return &Tree{
|
||||||
|
TreeCommon: TreeCommon{
|
||||||
ID: id,
|
ID: id,
|
||||||
repo: repo,
|
repo: repo,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,8 +11,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/go-git/go-git/v5/plumbing"
|
"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
|
// 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 {
|
if len(relpath) == 0 {
|
||||||
return &TreeEntry{
|
return &TreeEntry{
|
||||||
ID: t.ID,
|
ID: t.ID,
|
||||||
// Type: ObjectTree,
|
|
||||||
ptree: t,
|
ptree: t,
|
||||||
gogitTreeEntry: &object.TreeEntry{
|
name: "",
|
||||||
Name: "",
|
entryMode: EntryModeTree,
|
||||||
Mode: filemode.Dir,
|
|
||||||
Hash: plumbing.Hash(t.ID.RawValue()),
|
|
||||||
},
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,12 +6,60 @@ package git
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"path"
|
"path"
|
||||||
"sort"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/util"
|
"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)
|
// Type returns the type of the entry (commit, tree, blob)
|
||||||
func (te *TreeEntry) Type() string {
|
func (te *TreeEntry) Type() string {
|
||||||
switch te.Mode() {
|
switch te.Mode() {
|
||||||
@@ -109,49 +157,16 @@ func (te *TreeEntry) GetSubJumpablePathName() string {
|
|||||||
// Entries a list of entry
|
// Entries a list of entry
|
||||||
type Entries []*TreeEntry
|
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
|
// CustomSort customizable string comparing sort entry list
|
||||||
func (tes Entries) CustomSort(cmp func(s1, s2 string) bool) {
|
func (tes Entries) CustomSort(cmp func(s1, s2 string) int) {
|
||||||
sort.Sort(customSortableEntries{cmp, tes})
|
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())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,25 +12,21 @@ import (
|
|||||||
"github.com/go-git/go-git/v5/plumbing/object"
|
"github.com/go-git/go-git/v5/plumbing/object"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TreeEntry the leaf in the git tree
|
// gogitFileModeToEntryMode converts go-git filemode to EntryMode
|
||||||
type TreeEntry struct {
|
func gogitFileModeToEntryMode(mode filemode.FileMode) EntryMode {
|
||||||
ID ObjectID
|
return EntryMode(mode)
|
||||||
|
|
||||||
gogitTreeEntry *object.TreeEntry
|
|
||||||
ptree *Tree
|
|
||||||
|
|
||||||
size int64
|
|
||||||
sized bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Name returns the name of the entry
|
func entryModeToGogitFileMode(mode EntryMode) filemode.FileMode {
|
||||||
func (te *TreeEntry) Name() string {
|
return filemode.FileMode(mode)
|
||||||
return te.gogitTreeEntry.Name
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mode returns the mode of the entry
|
func (te *TreeEntry) toGogitTreeEntry() *object.TreeEntry {
|
||||||
func (te *TreeEntry) Mode() EntryMode {
|
return &object.TreeEntry{
|
||||||
return EntryMode(te.gogitTreeEntry.Mode)
|
Name: te.name,
|
||||||
|
Mode: entryModeToGogitFileMode(te.entryMode),
|
||||||
|
Hash: plumbing.Hash(te.ID.RawValue()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Size returns the size of the entry
|
// Size returns the size of the entry
|
||||||
@@ -41,7 +37,11 @@ func (te *TreeEntry) Size() int64 {
|
|||||||
return te.size
|
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 {
|
if err != nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@@ -51,40 +51,15 @@ func (te *TreeEntry) Size() int64 {
|
|||||||
return te.size
|
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
|
// Blob returns the blob object the entry
|
||||||
func (te *TreeEntry) Blob() *Blob {
|
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 {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Blob{
|
return &Blob{
|
||||||
ID: ParseGogitHash(te.gogitTreeEntry.Hash),
|
ID: te.ID,
|
||||||
gogitEncodedObj: encodedObj,
|
gogitEncodedObj: encodedObj,
|
||||||
name: te.Name(),
|
name: te.Name(),
|
||||||
}
|
}
|
||||||
|
|||||||
27
modules/git/tree_entry_gogit_test.go
Normal file
27
modules/git/tree_entry_gogit_test.go
Normal 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))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,27 +7,6 @@ package git
|
|||||||
|
|
||||||
import "code.gitea.io/gitea/modules/log"
|
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
|
// Size returns the size of the entry
|
||||||
func (te *TreeEntry) Size() int64 {
|
func (te *TreeEntry) Size() int64 {
|
||||||
if te.IsDir() {
|
if te.IsDir() {
|
||||||
@@ -57,31 +36,6 @@ func (te *TreeEntry) Size() int64 {
|
|||||||
return te.size
|
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
|
// Blob returns the blob object the entry
|
||||||
func (te *TreeEntry) Blob() *Blob {
|
func (te *TreeEntry) Blob() *Blob {
|
||||||
return &Blob{
|
return &Blob{
|
||||||
|
|||||||
@@ -1,55 +1,29 @@
|
|||||||
// Copyright 2017 The Gitea Authors. All rights reserved.
|
// Copyright 2017 The Gitea Authors. All rights reserved.
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
//go:build gogit
|
|
||||||
|
|
||||||
package git
|
package git
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math/rand/v2"
|
||||||
|
"slices"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/go-git/go-git/v5/plumbing/filemode"
|
|
||||||
"github.com/go-git/go-git/v5/plumbing/object"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"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) {
|
func TestEntriesCustomSort(t *testing.T) {
|
||||||
entries := getTestEntries()
|
entries := Entries{
|
||||||
entries.CustomSort(func(s1, s2 string) bool {
|
&TreeEntry{name: "a-dir", entryMode: EntryModeTree},
|
||||||
return s1 > s2
|
&TreeEntry{name: "a-submodule", entryMode: EntryModeCommit},
|
||||||
})
|
&TreeEntry{name: "b-dir", entryMode: EntryModeTree},
|
||||||
assert.Equal(t, "v2.2", entries[0].Name())
|
&TreeEntry{name: "b-submodule", entryMode: EntryModeCommit},
|
||||||
assert.Equal(t, "v2.12", entries[1].Name())
|
&TreeEntry{name: "a-file", entryMode: EntryModeBlob},
|
||||||
assert.Equal(t, "v2.1", entries[2].Name())
|
&TreeEntry{name: "b-file", entryMode: EntryModeBlob},
|
||||||
assert.Equal(t, "v2.0", entries[3].Name())
|
}
|
||||||
assert.Equal(t, "v12.0", entries[4].Name())
|
expected := slices.Clone(entries)
|
||||||
assert.Equal(t, "v1.0", entries[5].Name())
|
rand.Shuffle(len(entries), func(i, j int) { entries[i], entries[j] = entries[j], entries[i] })
|
||||||
assert.Equal(t, "bcd", entries[6].Name())
|
assert.NotEqual(t, expected, entries)
|
||||||
assert.Equal(t, "abc", entries[7].Name())
|
entries.CustomSort(strings.Compare)
|
||||||
|
assert.Equal(t, expected, entries)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,41 +15,34 @@ import (
|
|||||||
|
|
||||||
// Tree represents a flat directory listing.
|
// Tree represents a flat directory listing.
|
||||||
type Tree struct {
|
type Tree struct {
|
||||||
ID ObjectID
|
TreeCommon
|
||||||
ResolvedID ObjectID
|
|
||||||
repo *Repository
|
|
||||||
|
|
||||||
gogitTree *object.Tree
|
resolvedGogitTreeObject *object.Tree
|
||||||
|
|
||||||
// parent tree
|
|
||||||
ptree *Tree
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Tree) loadTreeObject() error {
|
func (t *Tree) gogitTreeObject() (_ *object.Tree, err error) {
|
||||||
gogitTree, err := t.repo.gogitRepo.TreeObject(plumbing.Hash(t.ID.RawValue()))
|
if t.resolvedGogitTreeObject == nil {
|
||||||
if err != nil {
|
t.resolvedGogitTreeObject, err = t.repo.gogitRepo.TreeObject(plumbing.Hash(t.ID.RawValue()))
|
||||||
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()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return t.resolvedGogitTreeObject, nil
|
||||||
|
}
|
||||||
|
|
||||||
entries := make([]*TreeEntry, len(t.gogitTree.Entries))
|
// ListEntries returns all entries of current tree.
|
||||||
for i, entry := range t.gogitTree.Entries {
|
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{
|
entries[i] = &TreeEntry{
|
||||||
ID: ParseGogitHash(entry.Hash),
|
ID: ParseGogitHash(gogitTreeEntry.Hash),
|
||||||
gogitTreeEntry: &t.gogitTree.Entries[i],
|
|
||||||
ptree: t,
|
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
|
// ListEntriesRecursiveWithSize returns all entries of current tree recursively including all subtrees
|
||||||
func (t *Tree) ListEntriesRecursiveWithSize() (Entries, error) {
|
func (t *Tree) ListEntriesRecursiveWithSize() (entries Entries, _ error) {
|
||||||
if t.gogitTree == nil {
|
gogitTree, err := t.gogitTreeObject()
|
||||||
err := t.loadTreeObject()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
var entries []*TreeEntry
|
walker := object.NewTreeWalker(gogitTree, true, nil)
|
||||||
seen := map[plumbing.Hash]bool{}
|
|
||||||
walker := object.NewTreeWalker(t.gogitTree, true, seen)
|
|
||||||
for {
|
for {
|
||||||
_, entry, err := walker.Next()
|
fullName, gogitTreeEntry, err := walker.Next()
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
break
|
break
|
||||||
}
|
} else if err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if seen[entry.Hash] {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
convertedEntry := &TreeEntry{
|
convertedEntry := &TreeEntry{
|
||||||
ID: ParseGogitHash(entry.Hash),
|
ID: ParseGogitHash(gogitTreeEntry.Hash),
|
||||||
gogitTreeEntry: &entry,
|
name: fullName, // FIXME: the "name" field is abused, here it is a full path
|
||||||
ptree: t,
|
ptree: t, // FIXME: this ptree is not right, fortunately it isn't really used
|
||||||
|
entryMode: gogitFileModeToEntryMode(gogitTreeEntry.Mode),
|
||||||
}
|
}
|
||||||
entries = append(entries, convertedEntry)
|
entries = append(entries, convertedEntry)
|
||||||
}
|
}
|
||||||
|
|
||||||
return entries, nil
|
return entries, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,18 +14,10 @@ import (
|
|||||||
|
|
||||||
// Tree represents a flat directory listing.
|
// Tree represents a flat directory listing.
|
||||||
type Tree struct {
|
type Tree struct {
|
||||||
ID ObjectID
|
TreeCommon
|
||||||
ResolvedID ObjectID
|
|
||||||
repo *Repository
|
|
||||||
|
|
||||||
// parent tree
|
|
||||||
ptree *Tree
|
|
||||||
|
|
||||||
entries Entries
|
entries Entries
|
||||||
entriesParsed bool
|
entriesParsed bool
|
||||||
|
|
||||||
entriesRecursive Entries
|
|
||||||
entriesRecursiveParsed bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListEntries returns all entries of current tree.
|
// 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
|
// listEntriesRecursive returns all entries of current tree recursively including all subtrees
|
||||||
// extraArgs could be "-l" to get the size, which is slower
|
// extraArgs could be "-l" to get the size, which is slower
|
||||||
func (t *Tree) listEntriesRecursive(extraArgs gitcmd.TrustedCmdArgs) (Entries, error) {
|
func (t *Tree) listEntriesRecursive(extraArgs gitcmd.TrustedCmdArgs) (Entries, error) {
|
||||||
if t.entriesRecursiveParsed {
|
|
||||||
return t.entriesRecursive, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
stdout, _, runErr := gitcmd.NewCommand("ls-tree", "-t", "-r").
|
stdout, _, runErr := gitcmd.NewCommand("ls-tree", "-t", "-r").
|
||||||
AddArguments(extraArgs...).
|
AddArguments(extraArgs...).
|
||||||
AddDynamicArguments(t.ID.String()).
|
AddDynamicArguments(t.ID.String()).
|
||||||
@@ -107,13 +95,9 @@ func (t *Tree) listEntriesRecursive(extraArgs gitcmd.TrustedCmdArgs) (Entries, e
|
|||||||
return nil, runErr
|
return nil, runErr
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
// FIXME: the "name" field is abused, here it is a full path
|
||||||
t.entriesRecursive, err = parseTreeEntries(stdout, t)
|
// FIXME: this ptree is not right, fortunately it isn't really used
|
||||||
if err == nil {
|
return parseTreeEntries(stdout, t)
|
||||||
t.entriesRecursiveParsed = true
|
|
||||||
}
|
|
||||||
|
|
||||||
return t.entriesRecursive, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListEntriesRecursiveFast returns all entries of current tree recursively including all subtrees, no size
|
// ListEntriesRecursiveFast returns all entries of current tree recursively including all subtrees, no size
|
||||||
|
|||||||
@@ -415,6 +415,8 @@ func Diff(ctx *context.Context) {
|
|||||||
ctx.ServerError("PostProcessCommitMessage", err)
|
ctx.ServerError("PostProcessCommitMessage", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
} else if !git.IsErrNotExist(err) {
|
||||||
|
log.Error("GetNote: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
pr, _ := issues_model.GetPullRequestByMergedCommit(ctx, ctx.Repo.Repository.ID, commitID)
|
pr, _ := issues_model.GetPullRequestByMergedCommit(ctx, ctx.Repo.Repository.ID, commitID)
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ func TreeList(ctx *context.Context) {
|
|||||||
ctx.ServerError("ListEntriesRecursiveFast", err)
|
ctx.ServerError("ListEntriesRecursiveFast", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
entries.CustomSort(base.NaturalSortLess)
|
entries.CustomSort(base.NaturalSortCompare)
|
||||||
|
|
||||||
files := make([]string, 0, len(entries))
|
files := make([]string, 0, len(entries))
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
|
|||||||
@@ -307,7 +307,7 @@ func renderDirectoryFiles(ctx *context.Context, timeout time.Duration) git.Entri
|
|||||||
ctx.ServerError("ListEntries", err)
|
ctx.ServerError("ListEntries", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
allEntries.CustomSort(base.NaturalSortLess)
|
allEntries.CustomSort(base.NaturalSortCompare)
|
||||||
|
|
||||||
commitInfoCtx := gocontext.Context(ctx)
|
commitInfoCtx := gocontext.Context(ctx)
|
||||||
if timeout > 0 {
|
if timeout > 0 {
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ func findReadmeFileInEntries(ctx *context.Context, parentDir string, entries []*
|
|||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
if i, ok := util.IsReadmeFileExtension(entry.Name(), exts...); ok {
|
if i, ok := util.IsReadmeFileExtension(entry.Name(), exts...); ok {
|
||||||
fullPath := path.Join(parentDir, entry.Name())
|
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() {
|
if entry.IsLink() {
|
||||||
res, err := git.EntryFollowLinks(ctx.Repo.Commit, fullPath, entry)
|
res, err := git.EntryFollowLinks(ctx.Repo.Commit, fullPath, entry)
|
||||||
if err == nil && (res.TargetEntry.IsExecutable() || res.TargetEntry.IsRegular()) {
|
if err == nil && (res.TargetEntry.IsExecutable() || res.TargetEntry.IsRegular()) {
|
||||||
|
|||||||
@@ -567,7 +567,7 @@ func WikiPages(ctx *context.Context) {
|
|||||||
ctx.ServerError("ListEntries", err)
|
ctx.ServerError("ListEntries", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
allEntries.CustomSort(base.NaturalSortLess)
|
allEntries.CustomSort(base.NaturalSortCompare)
|
||||||
|
|
||||||
entries, _, err := allEntries.GetCommitsInfo(ctx, ctx.Repo.RepoLink, commit, treePath)
|
entries, _, err := allEntries.GetCommitsInfo(ctx, ctx.Repo.RepoLink, commit, treePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user