1
1
mirror of https://github.com/go-gitea/gitea synced 2025-08-01 15:18:37 +00:00

Fix repo file list partial reloading for submodules (#35183)

Fix the TODO and add more tests
This commit is contained in:
wxiaoguang
2025-07-31 09:34:51 +08:00
committed by GitHub
parent 2e8a4a09d5
commit c3f5ea3b1f
6 changed files with 40 additions and 32 deletions

View File

@@ -3,8 +3,6 @@
package git package git
import "path"
// CommitInfo describes the first commit with the provided entry // CommitInfo describes the first commit with the provided entry
type CommitInfo struct { type CommitInfo struct {
Entry *TreeEntry Entry *TreeEntry
@@ -12,15 +10,14 @@ type CommitInfo struct {
SubmoduleFile *CommitSubmoduleFile SubmoduleFile *CommitSubmoduleFile
} }
func getCommitInfoSubmoduleFile(repoLink string, entry *TreeEntry, commit *Commit, treePathDir string) (*CommitSubmoduleFile, error) { func GetCommitInfoSubmoduleFile(repoLink, fullPath string, commit *Commit, refCommitID ObjectID) (*CommitSubmoduleFile, error) {
fullPath := path.Join(treePathDir, entry.Name())
submodule, err := commit.GetSubModule(fullPath) submodule, err := commit.GetSubModule(fullPath)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if submodule == nil { if submodule == nil {
// unable to find submodule from ".gitmodules" file // unable to find submodule from ".gitmodules" file
return NewCommitSubmoduleFile(repoLink, fullPath, "", entry.ID.String()), nil return NewCommitSubmoduleFile(repoLink, fullPath, "", refCommitID.String()), nil
} }
return NewCommitSubmoduleFile(repoLink, fullPath, submodule.URL, entry.ID.String()), nil return NewCommitSubmoduleFile(repoLink, fullPath, submodule.URL, refCommitID.String()), nil
} }

View File

@@ -73,7 +73,7 @@ func (tes Entries) GetCommitsInfo(ctx context.Context, repoLink string, commit *
// If the entry is a submodule, add a submodule file for this // If the entry is a submodule, add a submodule file for this
if entry.IsSubModule() { if entry.IsSubModule() {
commitsInfo[i].SubmoduleFile, err = getCommitInfoSubmoduleFile(repoLink, entry, commit, treePath) commitsInfo[i].SubmoduleFile, err = GetCommitInfoSubmoduleFile(repoLink, path.Join(treePath, entry.Name()), commit, entry.ID)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }

View File

@@ -64,7 +64,7 @@ func (tes Entries) GetCommitsInfo(ctx context.Context, repoLink string, commit *
// If the entry is a submodule, add a submodule file for this // If the entry is a submodule, add a submodule file for this
if entry.IsSubModule() { if entry.IsSubModule() {
commitsInfo[i].SubmoduleFile, err = getCommitInfoSubmoduleFile(repoLink, entry, commit, treePath) commitsInfo[i].SubmoduleFile, err = GetCommitInfoSubmoduleFile(repoLink, path.Join(treePath, entry.Name()), commit, entry.ID)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }

View File

@@ -125,9 +125,9 @@ func TestEntries_GetCommitsInfo(t *testing.T) {
t.Run("NonExistingSubmoduleAsNil", func(t *testing.T) { t.Run("NonExistingSubmoduleAsNil", func(t *testing.T) {
commit, err := bareRepo1.GetCommit("HEAD") commit, err := bareRepo1.GetCommit("HEAD")
require.NoError(t, err) require.NoError(t, err)
tree, err := commit.GetTreeEntryByPath("file1.txt") treeEntry, err := commit.GetTreeEntryByPath("file1.txt")
require.NoError(t, err) require.NoError(t, err)
cisf, err := getCommitInfoSubmoduleFile("/any/repo-link", tree, commit, "") cisf, err := GetCommitInfoSubmoduleFile("/any/repo-link", "file1.txt", commit, treeEntry.ID)
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, &CommitSubmoduleFile{ assert.Equal(t, &CommitSubmoduleFile{
repoLink: "/any/repo-link", repoLink: "/any/repo-link",

View File

@@ -19,8 +19,8 @@ import (
unit_model "code.gitea.io/gitea/models/unit" unit_model "code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
giturl "code.gitea.io/gitea/modules/git/url"
"code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/htmlutil"
"code.gitea.io/gitea/modules/httplib" "code.gitea.io/gitea/modules/httplib"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
repo_module "code.gitea.io/gitea/modules/repository" repo_module "code.gitea.io/gitea/modules/repository"
@@ -258,35 +258,41 @@ func handleRepoEmptyOrBroken(ctx *context.Context) {
ctx.Redirect(link) ctx.Redirect(link)
} }
func handleRepoViewSubmodule(ctx *context.Context, submodule *git.SubModule) { func isViewHomeOnlyContent(ctx *context.Context) bool {
// TODO: it needs to use git.NewCommitSubmoduleFile and SubmoduleWebLinkTree to correctly handle relative paths return ctx.FormBool("only_content")
submoduleRepoURL, err := giturl.ParseRepositoryURL(ctx, submodule.URL) }
if err != nil {
HandleGitError(ctx, "handleRepoViewSubmodule: ParseRepositoryURL", err) func handleRepoViewSubmodule(ctx *context.Context, commitSubmoduleFile *git.CommitSubmoduleFile) {
submoduleWebLink := commitSubmoduleFile.SubmoduleWebLinkTree(ctx)
if submoduleWebLink == nil {
ctx.Data["NotFoundPrompt"] = ctx.Repo.TreePath
ctx.NotFound(nil)
return return
} }
submoduleURL := giturl.MakeRepositoryWebLink(submoduleRepoURL)
if httplib.IsCurrentGiteaSiteURL(ctx, submoduleURL) { redirectLink := submoduleWebLink.CommitWebLink
ctx.RedirectToCurrentSite(submoduleURL) if isViewHomeOnlyContent(ctx) {
} else { ctx.Resp.Header().Set("Content-Type", "text/html; charset=utf-8")
_, _ = ctx.Resp.Write([]byte(htmlutil.HTMLFormat(`<a href="%s">%s</a>`, redirectLink, redirectLink)))
} else if !httplib.IsCurrentGiteaSiteURL(ctx, redirectLink) {
// don't auto-redirect to external URL, to avoid open redirect or phishing // don't auto-redirect to external URL, to avoid open redirect or phishing
ctx.Data["NotFoundPrompt"] = submoduleURL ctx.Data["NotFoundPrompt"] = redirectLink
ctx.NotFound(nil) ctx.NotFound(nil)
} else {
ctx.Redirect(submoduleWebLink.CommitWebLink)
} }
} }
func prepareToRenderDirOrFile(entry *git.TreeEntry) func(ctx *context.Context) { func prepareToRenderDirOrFile(entry *git.TreeEntry) func(ctx *context.Context) {
return func(ctx *context.Context) { return func(ctx *context.Context) {
if entry.IsSubModule() { if entry.IsSubModule() {
submodule, err := ctx.Repo.Commit.GetSubModule(entry.Name()) commitSubmoduleFile, err := git.GetCommitInfoSubmoduleFile(ctx.Repo.RepoLink, ctx.Repo.TreePath, ctx.Repo.Commit, entry.ID)
if err != nil { if err != nil {
HandleGitError(ctx, "prepareToRenderDirOrFile: GetSubModule", err) HandleGitError(ctx, "prepareToRenderDirOrFile: GetCommitInfoSubmoduleFile", err)
return return
} }
handleRepoViewSubmodule(ctx, submodule) handleRepoViewSubmodule(ctx, commitSubmoduleFile)
return } else if entry.IsDir() {
}
if entry.IsDir() {
prepareToRenderDirectory(ctx) prepareToRenderDirectory(ctx)
} else { } else {
prepareFileView(ctx, entry) prepareFileView(ctx, entry)
@@ -441,7 +447,7 @@ func Home(ctx *context.Context) {
} }
} }
if ctx.FormBool("only_content") { if isViewHomeOnlyContent(ctx) {
ctx.HTML(http.StatusOK, tplRepoViewContent) ctx.HTML(http.StatusOK, tplRepoViewContent)
} else if len(treeNames) != 0 { } else if len(treeNames) != 0 {
ctx.HTML(http.StatusOK, tplRepoView) ctx.HTML(http.StatusOK, tplRepoView)

View File

@@ -9,7 +9,6 @@ import (
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
git_module "code.gitea.io/gitea/modules/git" git_module "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/services/contexttest" "code.gitea.io/gitea/services/contexttest"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@@ -19,14 +18,20 @@ func TestViewHomeSubmoduleRedirect(t *testing.T) {
unittest.PrepareTestEnv(t) unittest.PrepareTestEnv(t)
ctx, _ := contexttest.MockContext(t, "/user2/repo1/src/branch/master/test-submodule") ctx, _ := contexttest.MockContext(t, "/user2/repo1/src/branch/master/test-submodule")
submodule := &git_module.SubModule{Path: "test-submodule", URL: setting.AppURL + "user2/repo-other.git"} submodule := git_module.NewCommitSubmoduleFile("/user2/repo1", "test-submodule", "../repo-other", "any-ref-id")
handleRepoViewSubmodule(ctx, submodule) handleRepoViewSubmodule(ctx, submodule)
assert.Equal(t, http.StatusSeeOther, ctx.Resp.WrittenStatus()) assert.Equal(t, http.StatusSeeOther, ctx.Resp.WrittenStatus())
assert.Equal(t, "/user2/repo-other", ctx.Resp.Header().Get("Location")) assert.Equal(t, "/user2/repo-other/tree/any-ref-id", ctx.Resp.Header().Get("Location"))
ctx, _ = contexttest.MockContext(t, "/user2/repo1/src/branch/master/test-submodule") ctx, _ = contexttest.MockContext(t, "/user2/repo1/src/branch/master/test-submodule")
submodule = &git_module.SubModule{Path: "test-submodule", URL: "https://other/user2/repo-other.git"} submodule = git_module.NewCommitSubmoduleFile("/user2/repo1", "test-submodule", "https://other/user2/repo-other.git", "any-ref-id")
handleRepoViewSubmodule(ctx, submodule) handleRepoViewSubmodule(ctx, submodule)
// do not auto-redirect for external URLs, to avoid open redirect or phishing // do not auto-redirect for external URLs, to avoid open redirect or phishing
assert.Equal(t, http.StatusNotFound, ctx.Resp.WrittenStatus()) assert.Equal(t, http.StatusNotFound, ctx.Resp.WrittenStatus())
ctx, respWriter := contexttest.MockContext(t, "/user2/repo1/src/branch/master/test-submodule?only_content=true")
submodule = git_module.NewCommitSubmoduleFile("/user2/repo1", "test-submodule", "../repo-other", "any-ref-id")
handleRepoViewSubmodule(ctx, submodule)
assert.Equal(t, http.StatusOK, ctx.Resp.WrittenStatus())
assert.Equal(t, `<a href="/user2/repo-other/tree/any-ref-id">/user2/repo-other/tree/any-ref-id</a>`, respWriter.Body.String())
} }