1
0
mirror of https://github.com/go-gitea/gitea.git synced 2025-01-02 01:17:43 +03:00

refactor git repo usage

This commit is contained in:
wxiaoguang 2024-12-30 19:58:47 +08:00
parent 586d304a92
commit 0202a09105
16 changed files with 83 additions and 63 deletions

View File

@ -43,19 +43,20 @@ type contextKey struct {
}
// RepositoryFromContextOrOpen attempts to get the repository from the context or just opens it
// The caller must call "defer gitRepo.Close()"
func RepositoryFromContextOrOpen(ctx context.Context, repo Repository) (*git.Repository, io.Closer, error) {
ds := reqctx.GetRequestDataStore(ctx)
if ds != nil {
gitRepo, err := RepositoryFromRequestContextOrOpen(ctx, ds, repo)
reqCtx := reqctx.FromContext(ctx)
if reqCtx != nil {
gitRepo, err := RepositoryFromRequestContextOrOpen(reqCtx, repo)
return gitRepo, util.NopCloser{}, err
}
gitRepo, err := OpenRepository(ctx, repo)
return gitRepo, gitRepo, err
}
// RepositoryFromRequestContextOrOpen opens the repository at the given relative path in the provided request context
// The repo will be automatically closed when the request context is done
func RepositoryFromRequestContextOrOpen(ctx context.Context, ds reqctx.RequestDataStore, repo Repository) (*git.Repository, error) {
// RepositoryFromRequestContextOrOpen opens the repository at the given relative path in the provided request context.
// Caller shouldn't close the git repo manually, the git repo will be automatically closed when the request context is done.
func RepositoryFromRequestContextOrOpen(ctx reqctx.RequestContext, repo Repository) (*git.Repository, error) {
ck := contextKey{repoPath: repoPath(repo)}
if gitRepo, ok := ctx.Value(ck).(*git.Repository); ok {
return gitRepo, nil
@ -64,7 +65,7 @@ func RepositoryFromRequestContextOrOpen(ctx context.Context, ds reqctx.RequestDa
if err != nil {
return nil, err
}
ds.AddCloser(gitRepo)
ds.SetContextValue(ck, gitRepo)
ctx.AddCloser(gitRepo)
ctx.SetContextValue(ck, gitRepo)
return gitRepo, nil
}

View File

@ -88,6 +88,21 @@ func (r *requestDataStore) cleanUp() {
}
}
type RequestContext interface {
context.Context
RequestDataStore
}
func FromContext(ctx context.Context) RequestContext {
// here we must use the current ctx and the underlying store
// the current ctx guarantees that the ctx deadline/cancellation/values are respected
// the underlying store guarantees that the request-specific data is available
if store := GetRequestDataStore(ctx); store != nil {
return &requestContext{Context: ctx, RequestDataStore: store}
}
return nil
}
func GetRequestDataStore(ctx context.Context) RequestDataStore {
if req, ok := ctx.Value(RequestDataStoreKey).(*requestDataStore); ok {
return req
@ -97,11 +112,11 @@ func GetRequestDataStore(ctx context.Context) RequestDataStore {
type requestContext struct {
context.Context
dataStore *requestDataStore
RequestDataStore
}
func (c *requestContext) Value(key any) any {
if v := c.dataStore.GetContextValue(key); v != nil {
if v := c.GetContextValue(key); v != nil {
return v
}
return c.Context.Value(key)
@ -109,9 +124,10 @@ func (c *requestContext) Value(key any) any {
func NewRequestContext(parentCtx context.Context, profDesc string) (_ context.Context, finished func()) {
ctx, _, processFinished := process.GetManager().AddTypedContext(parentCtx, profDesc, process.RequestProcessType, true)
reqCtx := &requestContext{Context: ctx, dataStore: &requestDataStore{values: make(map[any]any)}}
store := &requestDataStore{values: make(map[any]any)}
reqCtx := &requestContext{Context: ctx, RequestDataStore: store}
return reqCtx, func() {
reqCtx.dataStore.cleanUp()
store.cleanUp()
processFinished()
}
}
@ -119,5 +135,5 @@ func NewRequestContext(parentCtx context.Context, profDesc string) (_ context.Co
// NewRequestContextForTest creates a new RequestContext for testing purposes
// It doesn't add the context to the process manager, nor do cleanup
func NewRequestContextForTest(parentCtx context.Context) context.Context {
return &requestContext{Context: parentCtx, dataStore: &requestDataStore{values: make(map[any]any)}}
return &requestContext{Context: parentCtx, RequestDataStore: &requestDataStore{values: make(map[any]any)}}
}

View File

@ -286,7 +286,6 @@ func QueryBuild(a ...any) template.URL {
reqPath = s1 + "?"
s = s2
}
}
for i := len(a) % 2; i < len(a); i += 2 {
k, ok := a[i].(string)

View File

@ -729,7 +729,7 @@ func CreateBranchProtection(ctx *context.APIContext) {
} else {
if !isPlainRule {
if ctx.Repo.GitRepo == nil {
ctx.Repo.GitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, ctx, ctx.Repo.Repository)
ctx.Repo.GitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, ctx.Repo.Repository)
if err != nil {
ctx.Error(http.StatusInternalServerError, "OpenRepository", err)
return
@ -1057,7 +1057,7 @@ func EditBranchProtection(ctx *context.APIContext) {
} else {
if !isPlainRule {
if ctx.Repo.GitRepo == nil {
ctx.Repo.GitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, ctx, ctx.Repo.Repository)
ctx.Repo.GitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, ctx.Repo.Repository)
if err != nil {
ctx.Error(http.StatusInternalServerError, "OpenRepository", err)
return

View File

@ -45,7 +45,7 @@ func CompareDiff(ctx *context.APIContext) {
if ctx.Repo.GitRepo == nil {
var err error
ctx.Repo.GitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, ctx, ctx.Repo.Repository)
ctx.Repo.GitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, ctx.Repo.Repository)
if err != nil {
ctx.Error(http.StatusInternalServerError, "OpenRepository", err)
return

View File

@ -29,7 +29,7 @@ func DownloadArchive(ctx *context.APIContext) {
if ctx.Repo.GitRepo == nil {
var err error
ctx.Repo.GitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, ctx, ctx.Repo.Repository)
ctx.Repo.GitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, ctx.Repo.Repository)
if err != nil {
ctx.Error(http.StatusInternalServerError, "OpenRepository", err)
return

View File

@ -282,7 +282,7 @@ func GetArchive(ctx *context.APIContext) {
if ctx.Repo.GitRepo == nil {
var err error
ctx.Repo.GitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, ctx, ctx.Repo.Repository)
ctx.Repo.GitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, ctx.Repo.Repository)
if err != nil {
ctx.Error(http.StatusInternalServerError, "OpenRepository", err)
return

View File

@ -726,7 +726,7 @@ func updateBasicProperties(ctx *context.APIContext, opts api.EditRepoOption) err
if ctx.Repo.GitRepo == nil && !repo.IsEmpty {
var err error
ctx.Repo.GitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, ctx, repo)
ctx.Repo.GitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, repo)
if err != nil {
ctx.Error(http.StatusInternalServerError, "Unable to OpenRepository", err)
return err

View File

@ -27,7 +27,7 @@ func RepoAssignment(ctx *gitea_context.PrivateContext) {
return
}
gitRepo, err := gitrepo.RepositoryFromRequestContextOrOpen(ctx, ctx, repo)
gitRepo, err := gitrepo.RepositoryFromRequestContextOrOpen(ctx, repo)
if err != nil {
log.Error("Failed to open repository: %s/%s Error: %v", ownerName, repoName, err)
ctx.JSON(http.StatusInternalServerError, private.Response{

View File

@ -178,8 +178,7 @@ type prepareOrgProfileReadmeOptions struct {
func prepareOrgProfileReadme(ctx *context.Context, opts prepareOrgProfileReadmeOptions) bool {
profileRepoName := util.Iif(opts.viewAsPrivate, shared_user.RepoNameProfilePrivate, shared_user.RepoNameProfile)
profileDbRepo, profileGitRepo, profileReadme, profileClose := shared_user.FindOwnerProfileReadme(ctx, ctx.Doer, profileRepoName)
defer profileClose()
profileDbRepo, profileReadme := shared_user.FindOwnerProfileReadme(ctx, ctx.Doer, profileRepoName)
if opts.viewAsPrivate {
ctx.Data["HasPrivateProfileReadme"] = profileReadme != nil
@ -187,7 +186,7 @@ func prepareOrgProfileReadme(ctx *context.Context, opts prepareOrgProfileReadmeO
ctx.Data["HasPublicProfileReadme"] = profileReadme != nil
}
if profileGitRepo == nil || profileReadme == nil || opts.viewRepositories {
if profileReadme == nil || opts.viewRepositories {
return false
}

View File

@ -103,37 +103,45 @@ func PrepareContextForProfileBigAvatar(ctx *context.Context) {
}
}
func FindOwnerProfileReadme(ctx *context.Context, doer *user_model.User, optProfileRepoName ...string) (profileDbRepo *repo_model.Repository, profileGitRepo *git.Repository, profileReadmeBlob *git.Blob, profileClose func()) {
profileRepoName := util.OptionalArg(optProfileRepoName, ".profile")
func FindOwnerProfileReadme(ctx *context.Context, doer *user_model.User, optProfileRepoName ...string) (profileDbRepo *repo_model.Repository, profileReadmeBlob *git.Blob) {
profileRepoName := util.OptionalArg(optProfileRepoName, RepoNameProfile)
profileDbRepo, err := repo_model.GetRepositoryByName(ctx, ctx.ContextUser.ID, profileRepoName)
if err == nil {
perm, err := access_model.GetUserRepoPermission(ctx, profileDbRepo, doer)
if err == nil && !profileDbRepo.IsEmpty && perm.CanRead(unit.TypeCode) {
if profileGitRepo, err = gitrepo.OpenRepository(ctx, profileDbRepo); err != nil {
log.Error("FindOwnerProfileReadme failed to OpenRepository: %v", err)
} else {
if commit, err := profileGitRepo.GetBranchCommit(profileDbRepo.DefaultBranch); err != nil {
log.Error("FindOwnerProfileReadme failed to GetBranchCommit: %v", err)
} else {
profileReadmeBlob, _ = commit.GetBlobByPath("README.md")
}
}
if err != nil {
if !repo_model.IsErrRepoNotExist(err) {
log.Error("FindOwnerProfileReadme failed to GetRepositoryByName: %v", err)
}
} else if !repo_model.IsErrRepoNotExist(err) {
return nil, nil
}
perm, err := access_model.GetUserRepoPermission(ctx, profileDbRepo, doer)
if err != nil {
log.Error("FindOwnerProfileReadme failed to GetRepositoryByName: %v", err)
return nil, nil
}
return profileDbRepo, profileGitRepo, profileReadmeBlob, func() {
if profileGitRepo != nil {
_ = profileGitRepo.Close()
}
if profileDbRepo.IsEmpty || !perm.CanRead(unit.TypeCode) {
return nil, nil
}
profileGitRepo, err := gitrepo.RepositoryFromRequestContextOrOpen(ctx, profileDbRepo)
if err != nil {
log.Error("FindOwnerProfileReadme failed to OpenRepository: %v", err)
return nil, nil
}
commit, err := profileGitRepo.GetBranchCommit(profileDbRepo.DefaultBranch)
if err != nil {
log.Error("FindOwnerProfileReadme failed to GetBranchCommit: %v", err)
return nil, nil
}
profileReadmeBlob, _ = commit.GetBlobByPath("README.md") // no need to handle this error
return profileDbRepo, profileReadmeBlob
}
func RenderUserHeader(ctx *context.Context) {
prepareContextForCommonProfile(ctx)
_, _, profileReadmeBlob, profileClose := FindOwnerProfileReadme(ctx, ctx.Doer)
defer profileClose()
_, profileReadmeBlob := FindOwnerProfileReadme(ctx, ctx.Doer)
ctx.Data["HasPublicProfileReadme"] = profileReadmeBlob != nil
}
@ -182,13 +190,11 @@ func RenderOrgHeader(ctx *context.Context) error {
}
// FIXME: only do database query, do not open it
_, _, profileReadmeBlob, profileClose := FindOwnerProfileReadme(ctx, ctx.Doer)
defer profileClose()
_, profileReadmeBlob := FindOwnerProfileReadme(ctx, ctx.Doer)
ctx.Data["HasPublicProfileReadme"] = profileReadmeBlob != nil
// FIXME: only do database query, do not open it
_, _, profileReadmeBlob, profileClose = FindOwnerProfileReadme(ctx, ctx.Doer, RepoNameProfilePrivate)
defer profileClose()
_, profileReadmeBlob = FindOwnerProfileReadme(ctx, ctx.Doer, RepoNameProfilePrivate)
ctx.Data["HasPrivateProfileReadme"] = profileReadmeBlob != nil
return nil

View File

@ -74,8 +74,7 @@ func userProfile(ctx *context.Context) {
ctx.Data["HeatmapTotalContributions"] = activities_model.GetTotalContributionsInHeatmap(data)
}
profileDbRepo, _ /*profileGitRepo*/, profileReadmeBlob, profileClose := shared_user.FindOwnerProfileReadme(ctx, ctx.Doer)
defer profileClose()
profileDbRepo, profileReadmeBlob := shared_user.FindOwnerProfileReadme(ctx, ctx.Doer)
showPrivate := ctx.IsSigned && (ctx.Doer.IsAdmin || ctx.Doer.ID == ctx.ContextUser.ID)
prepareUserProfileTabData(ctx, showPrivate, profileDbRepo, profileReadmeBlob)

View File

@ -274,7 +274,7 @@ func ReferencesGitRepo(allowEmpty ...bool) func(ctx *APIContext) {
// For API calls.
if ctx.Repo.GitRepo == nil {
var err error
ctx.Repo.GitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, ctx, ctx.Repo.Repository)
ctx.Repo.GitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, ctx.Repo.Repository)
if err != nil {
ctx.Error(http.StatusInternalServerError, fmt.Sprintf("Open Repository %v failed", ctx.Repo.Repository.FullName()), err)
return

View File

@ -622,7 +622,7 @@ func RepoAssignment(ctx *Context) {
ctx.Repo.GitRepo = nil
}
ctx.Repo.GitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, ctx, repo)
ctx.Repo.GitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, repo)
if err != nil {
if strings.Contains(err.Error(), "repository does not exist") || strings.Contains(err.Error(), "no such file or directory") {
log.Error("Repository %-v has a broken repository on the file system: %s Error: %v", ctx.Repo.Repository, ctx.Repo.Repository.RepoPath(), err)
@ -881,7 +881,7 @@ func RepoRefByType(detectRefType RepoRefType, opts ...RepoRefByTypeOptions) func
)
if ctx.Repo.GitRepo == nil {
ctx.Repo.GitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, ctx, ctx.Repo.Repository)
ctx.Repo.GitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, ctx.Repo.Repository)
if err != nil {
ctx.ServerError(fmt.Sprintf("Open Repository %v failed", ctx.Repo.Repository.FullName()), err)
return

View File

@ -8,7 +8,7 @@
{{if or .ProfileReadmeContent}}
{{if and .ShowMemberAndTeamTab .HasPublicProfileReadme .HasPrivateProfileReadme}}
<div class="ui small secondary filter menu">
<div id="profile_view_as_dropdown" class="item ui small dropdown jump">
<div id="org-home-view-as-dropdown" class="item ui small dropdown jump">
<span class="text">
{{svg "octicon-eye" 14}}
View as: {{if not .IsViewerMember}}{{ctx.Locale.Tr "settings.visibility.public"}}{{else}}{{ctx.Locale.Tr "org.members.member"}}{{end}}

View File

@ -58,7 +58,7 @@ func TestOrgProfile(t *testing.T) {
func testOrgProfile(t *testing.T, u *url.URL) {
// html #user-content-public (markdown title of public profile)
// html #user-content-private (markdown title of private profile)
// html #profile_view_as_dropdown (indicate whether the view as dropdown menu is present)
// html #org-home-view-as-dropdown (indicate whether the view as dropdown menu is present)
// PART 1: Test Both Private and Public
createTestProfile(t, "org3", user.RepoNameProfile, "Public Readme")
@ -72,7 +72,7 @@ func testOrgProfile(t *testing.T, u *url.URL) {
profileDivs := htmlDoc.doc.Find("#user-content-public")
assert.EqualValues(t, 1, profileDivs.Length())
dropDownDiv := htmlDoc.doc.Find("#profile_view_as_dropdown")
dropDownDiv := htmlDoc.doc.Find("#org-home-view-as-dropdown")
assert.EqualValues(t, 0, dropDownDiv.Length())
// Logged in but not member
@ -84,7 +84,7 @@ func testOrgProfile(t *testing.T, u *url.URL) {
profileDivs = htmlDoc.doc.Find("#user-content-public")
assert.EqualValues(t, 1, profileDivs.Length())
dropDownDiv = htmlDoc.doc.Find("#profile_view_as_dropdown")
dropDownDiv = htmlDoc.doc.Find("#org-home-view-as-dropdown")
assert.EqualValues(t, 0, dropDownDiv.Length())
// Site Admin
@ -96,7 +96,7 @@ func testOrgProfile(t *testing.T, u *url.URL) {
profileDivs = htmlDoc.doc.Find("#user-content-public")
assert.EqualValues(t, 1, profileDivs.Length())
dropDownDiv = htmlDoc.doc.Find("#profile_view_as_dropdown")
dropDownDiv = htmlDoc.doc.Find("#org-home-view-as-dropdown")
assert.EqualValues(t, 1, dropDownDiv.Length())
req = NewRequest(t, "GET", "/org3?view_as=member")
@ -123,7 +123,7 @@ func testOrgProfile(t *testing.T, u *url.URL) {
htmlDoc = NewHTMLParser(t, resp.Body)
profileDivs = htmlDoc.doc.Find("#user-content-public")
assert.EqualValues(t, 1, profileDivs.Length())
dropDownDiv = htmlDoc.doc.Find("#profile_view_as_dropdown")
dropDownDiv = htmlDoc.doc.Find("#org-home-view-as-dropdown")
assert.EqualValues(t, 0, dropDownDiv.Length())
req = NewRequest(t, "GET", "/org42")
@ -133,7 +133,7 @@ func testOrgProfile(t *testing.T, u *url.URL) {
assert.EqualValues(t, 0, profileDivs.Length())
profileDivs = htmlDoc.doc.Find("#user-content-public")
assert.EqualValues(t, 0, profileDivs.Length())
dropDownDiv = htmlDoc.doc.Find("#profile_view_as_dropdown")
dropDownDiv = htmlDoc.doc.Find("#org-home-view-as-dropdown")
assert.EqualValues(t, 0, dropDownDiv.Length())
// Site Admin
@ -142,7 +142,7 @@ func testOrgProfile(t *testing.T, u *url.URL) {
htmlDoc = NewHTMLParser(t, resp.Body)
profileDivs = htmlDoc.doc.Find("#user-content-public")
assert.EqualValues(t, 1, profileDivs.Length())
dropDownDiv = htmlDoc.doc.Find("#profile_view_as_dropdown")
dropDownDiv = htmlDoc.doc.Find("#org-home-view-as-dropdown")
assert.EqualValues(t, 0, dropDownDiv.Length())
req = NewRequest(t, "GET", "/org42")
@ -150,6 +150,6 @@ func testOrgProfile(t *testing.T, u *url.URL) {
htmlDoc = NewHTMLParser(t, resp.Body)
profileDivs = htmlDoc.doc.Find("#user-content-private")
assert.EqualValues(t, 1, profileDivs.Length())
dropDownDiv = htmlDoc.doc.Find("#profile_view_as_dropdown")
dropDownDiv = htmlDoc.doc.Find("#org-home-view-as-dropdown")
assert.EqualValues(t, 0, dropDownDiv.Length())
}