mirror of https://github.com/go-gitea/gitea.git
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>pull/35868/head^2
parent
d0ca2f6bc3
commit
525265c1a8
@ -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 (
|
||||||
@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue