Skip to content

Commit 6cb22e5

Browse files
authored
Merge pull request #2 from lunny/add-file-tree-to-file-view-page
Add some backend logic
2 parents e46f7e4 + 023a260 commit 6cb22e5

File tree

4 files changed

+160
-4
lines changed

4 files changed

+160
-4
lines changed

routers/web/repo/treelist.go renamed to routers/web/repo/tree.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"code.gitea.io/gitea/modules/base"
1010
"code.gitea.io/gitea/modules/git"
1111
"code.gitea.io/gitea/services/context"
12+
files_service "code.gitea.io/gitea/services/repository/files"
1213

1314
"github.com/go-enry/go-enry/v2"
1415
)
@@ -52,3 +53,18 @@ func isExcludedEntry(entry *git.TreeEntry) bool {
5253

5354
return false
5455
}
56+
57+
func Tree(ctx *context.Context) {
58+
dir := ctx.PathParam("*")
59+
ref := ctx.FormTrim("ref")
60+
recursive := ctx.FormBool("recursive")
61+
62+
// TODO: Only support branch for now
63+
results, err := files_service.GetTreeList(ctx, ctx.Repo.Repository, dir, git.RefNameFromBranch(ref), recursive)
64+
if err != nil {
65+
ctx.ServerError("guessRefInfoAndDir", err)
66+
return
67+
}
68+
69+
ctx.JSON(http.StatusOK, results)
70+
}

routers/web/web.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1157,14 +1157,14 @@ func registerRoutes(m *web.Router) {
11571157

11581158
m.Group("/{username}/{reponame}", func() {
11591159
m.Get("/find/*", repo.FindFiles)
1160-
m.Group("/tree-list", func() {
1160+
m.Group("/tree-list", func() { // for find files
11611161
m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.TreeList)
11621162
m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.TreeList)
11631163
m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.TreeList)
11641164
})
1165-
m.Group("/contents", func() {
1166-
m.Get("", repo.GetContentsList)
1167-
m.Get("/*", repo.GetContents)
1165+
m.Group("/tree", func() {
1166+
m.Get("", repo.Tree)
1167+
m.Get("/*", repo.Tree)
11681168
})
11691169
m.Get("/compare", repo.MustBeNotEmpty, repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.CompareDiff)
11701170
m.Combo("/compare/*", repo.MustBeNotEmpty, repo.SetEditorconfigIfExists).

services/repository/files/tree.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@ import (
77
"context"
88
"fmt"
99
"net/url"
10+
"path"
11+
"strings"
1012

1113
repo_model "code.gitea.io/gitea/models/repo"
1214
"code.gitea.io/gitea/modules/git"
15+
"code.gitea.io/gitea/modules/gitrepo"
1316
"code.gitea.io/gitea/modules/setting"
1417
api "code.gitea.io/gitea/modules/structs"
1518
"code.gitea.io/gitea/modules/util"
@@ -118,3 +121,92 @@ func GetTreeBySHA(ctx context.Context, repo *repo_model.Repository, gitRepo *git
118121
}
119122
return tree, nil
120123
}
124+
125+
type TreeEntry struct {
126+
Name string `json:"name"`
127+
IsFile bool `json:"isFile"`
128+
Path string `json:"path"`
129+
Children []*TreeEntry `json:"children"`
130+
}
131+
132+
func GetTreeList(ctx context.Context, repo *repo_model.Repository, treePath string, ref git.RefName, recursive bool) ([]*TreeEntry, error) {
133+
if repo.IsEmpty {
134+
return nil, nil
135+
}
136+
if ref == "" {
137+
ref = git.RefNameFromBranch(repo.DefaultBranch)
138+
}
139+
140+
// Check that the path given in opts.treePath is valid (not a git path)
141+
cleanTreePath := CleanUploadFileName(treePath)
142+
if cleanTreePath == "" && treePath != "" {
143+
return nil, models.ErrFilenameInvalid{
144+
Path: treePath,
145+
}
146+
}
147+
treePath = cleanTreePath
148+
149+
gitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, repo)
150+
if err != nil {
151+
return nil, err
152+
}
153+
defer closer.Close()
154+
155+
// Get the commit object for the ref
156+
commit, err := gitRepo.GetCommit(ref.String())
157+
if err != nil {
158+
return nil, err
159+
}
160+
161+
entry, err := commit.GetTreeEntryByPath(treePath)
162+
if err != nil {
163+
return nil, err
164+
}
165+
166+
// If the entry is a file, we return a FileContentResponse object
167+
if entry.Type() != "tree" {
168+
return nil, fmt.Errorf("%s is not a tree", treePath)
169+
}
170+
171+
gitTree, err := commit.SubTree(treePath)
172+
if err != nil {
173+
return nil, err
174+
}
175+
var entries git.Entries
176+
if recursive {
177+
entries, err = gitTree.ListEntriesRecursiveFast()
178+
} else {
179+
entries, err = gitTree.ListEntries()
180+
}
181+
if err != nil {
182+
return nil, err
183+
}
184+
185+
var treeList []*TreeEntry
186+
mapTree := make(map[string][]*TreeEntry)
187+
for _, e := range entries {
188+
subTreePath := path.Join(treePath, e.Name())
189+
190+
if strings.Contains(e.Name(), "/") {
191+
mapTree[path.Dir(e.Name())] = append(mapTree[path.Dir(e.Name())], &TreeEntry{
192+
Name: path.Base(e.Name()),
193+
IsFile: e.Mode() != git.EntryModeTree,
194+
Path: subTreePath,
195+
})
196+
} else {
197+
treeList = append(treeList, &TreeEntry{
198+
Name: e.Name(),
199+
IsFile: e.Mode() != git.EntryModeTree,
200+
Path: subTreePath,
201+
})
202+
}
203+
}
204+
205+
for _, tree := range treeList {
206+
if !tree.IsFile {
207+
tree.Children = mapTree[tree.Path]
208+
}
209+
}
210+
211+
return treeList, nil
212+
}

services/repository/files/tree_test.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"testing"
88

99
"code.gitea.io/gitea/models/unittest"
10+
"code.gitea.io/gitea/modules/git"
1011
api "code.gitea.io/gitea/modules/structs"
1112
"code.gitea.io/gitea/services/contexttest"
1213

@@ -50,3 +51,50 @@ func TestGetTreeBySHA(t *testing.T) {
5051

5152
assert.EqualValues(t, expectedTree, tree)
5253
}
54+
55+
func Test_GetTreeList(t *testing.T) {
56+
unittest.PrepareTestEnv(t)
57+
ctx1, _ := contexttest.MockContext(t, "user2/repo1")
58+
contexttest.LoadRepo(t, ctx1, 1)
59+
contexttest.LoadRepoCommit(t, ctx1)
60+
contexttest.LoadUser(t, ctx1, 2)
61+
contexttest.LoadGitRepo(t, ctx1)
62+
defer ctx1.Repo.GitRepo.Close()
63+
64+
refName := git.RefNameFromBranch(ctx1.Repo.Repository.DefaultBranch)
65+
66+
treeList, err := GetTreeList(ctx1, ctx1.Repo.Repository, "", refName, true)
67+
assert.NoError(t, err)
68+
assert.Len(t, treeList, 1)
69+
assert.EqualValues(t, "README.md", treeList[0].Name)
70+
assert.EqualValues(t, "README.md", treeList[0].Path)
71+
assert.True(t, treeList[0].IsFile)
72+
assert.Empty(t, treeList[0].Children)
73+
74+
ctx2, _ := contexttest.MockContext(t, "org3/repo3")
75+
contexttest.LoadRepo(t, ctx2, 3)
76+
contexttest.LoadRepoCommit(t, ctx2)
77+
contexttest.LoadUser(t, ctx2, 2)
78+
contexttest.LoadGitRepo(t, ctx2)
79+
defer ctx2.Repo.GitRepo.Close()
80+
81+
refName = git.RefNameFromBranch(ctx2.Repo.Repository.DefaultBranch)
82+
83+
treeList, err = GetTreeList(ctx2, ctx2.Repo.Repository, "", refName, true)
84+
assert.NoError(t, err)
85+
assert.Len(t, treeList, 2)
86+
assert.EqualValues(t, "README.md", treeList[0].Name)
87+
assert.EqualValues(t, "README.md", treeList[0].Path)
88+
assert.True(t, treeList[0].IsFile)
89+
assert.Empty(t, treeList[0].Children)
90+
91+
assert.EqualValues(t, "doc", treeList[1].Name)
92+
assert.EqualValues(t, "doc", treeList[1].Path)
93+
assert.False(t, treeList[1].IsFile)
94+
assert.Len(t, treeList[1].Children, 1)
95+
96+
assert.EqualValues(t, "doc.md", treeList[1].Children[0].Name)
97+
assert.EqualValues(t, "doc/doc.md", treeList[1].Children[0].Path)
98+
assert.True(t, treeList[1].Children[0].IsFile)
99+
assert.Empty(t, treeList[1].Children[0].Children)
100+
}

0 commit comments

Comments
 (0)