mirror of
https://github.com/go-gitea/gitea
synced 2025-07-23 02:38:35 +00:00
Refactor request context (#32956)
Introduce RequestContext: is a short-lived context that is used to store request-specific data. RequestContext could be used to clean form tmp files, close context git repo, and do some tracing in the future. Then a lot of legacy code could be removed or improved. For example: most `ctx.Repo.GitRepo.Close()` could be removed because the git repo could be closed when the request is done.
This commit is contained in:
@@ -5,7 +5,6 @@
|
||||
package context
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -212,17 +211,15 @@ func (ctx *APIContext) SetLinkHeader(total, pageSize int) {
|
||||
func APIContexter() func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
base, baseCleanUp := NewBaseContext(w, req)
|
||||
base := NewBaseContext(w, req)
|
||||
ctx := &APIContext{
|
||||
Base: base,
|
||||
Cache: cache.GetCache(),
|
||||
Repo: &Repository{PullRequest: &PullRequest{}},
|
||||
Org: &APIOrganization{},
|
||||
}
|
||||
defer baseCleanUp()
|
||||
|
||||
ctx.Base.AppendContextValue(apiContextKey, ctx)
|
||||
ctx.Base.AppendContextValueFunc(gitrepo.RepositoryContextKey, func() any { return ctx.Repo.GitRepo })
|
||||
ctx.SetContextValue(apiContextKey, ctx)
|
||||
|
||||
// If request sends files, parse them here otherwise the Query() can't be parsed and the CsrfToken will be invalid.
|
||||
if ctx.Req.Method == "POST" && strings.Contains(ctx.Req.Header.Get("Content-Type"), "multipart/form-data") {
|
||||
@@ -267,31 +264,22 @@ func (ctx *APIContext) NotFound(objs ...any) {
|
||||
|
||||
// ReferencesGitRepo injects the GitRepo into the Context
|
||||
// you can optional skip the IsEmpty check
|
||||
func ReferencesGitRepo(allowEmpty ...bool) func(ctx *APIContext) (cancel context.CancelFunc) {
|
||||
return func(ctx *APIContext) (cancel context.CancelFunc) {
|
||||
func ReferencesGitRepo(allowEmpty ...bool) func(ctx *APIContext) {
|
||||
return func(ctx *APIContext) {
|
||||
// Empty repository does not have reference information.
|
||||
if ctx.Repo.Repository.IsEmpty && !(len(allowEmpty) != 0 && allowEmpty[0]) {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
// For API calls.
|
||||
if ctx.Repo.GitRepo == nil {
|
||||
gitRepo, err := gitrepo.OpenRepository(ctx, ctx.Repo.Repository)
|
||||
var err error
|
||||
ctx.Repo.GitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, ctx, ctx.Repo.Repository)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, fmt.Sprintf("Open Repository %v failed", ctx.Repo.Repository.FullName()), err)
|
||||
return cancel
|
||||
}
|
||||
ctx.Repo.GitRepo = gitRepo
|
||||
// We opened it, we should close it
|
||||
return func() {
|
||||
// If it's been set to nil then assume someone else has closed it.
|
||||
if ctx.Repo.GitRepo != nil {
|
||||
_ = ctx.Repo.GitRepo.Close()
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return cancel
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -12,12 +12,12 @@ import (
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/httplib"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
"code.gitea.io/gitea/modules/reqctx"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/translation"
|
||||
"code.gitea.io/gitea/modules/web/middleware"
|
||||
@@ -25,65 +25,25 @@ import (
|
||||
"github.com/go-chi/chi/v5"
|
||||
)
|
||||
|
||||
type contextValuePair struct {
|
||||
key any
|
||||
valueFn func() any
|
||||
}
|
||||
|
||||
type BaseContextKeyType struct{}
|
||||
|
||||
var BaseContextKey BaseContextKeyType
|
||||
|
||||
type Base struct {
|
||||
originCtx context.Context
|
||||
contextValues []contextValuePair
|
||||
context.Context
|
||||
reqctx.RequestDataStore
|
||||
|
||||
Resp ResponseWriter
|
||||
Req *http.Request
|
||||
|
||||
// Data is prepared by ContextDataStore middleware, this field only refers to the pre-created/prepared ContextData.
|
||||
// Although it's mainly used for MVC templates, sometimes it's also used to pass data between middlewares/handler
|
||||
Data middleware.ContextData
|
||||
Data reqctx.ContextData
|
||||
|
||||
// Locale is mainly for Web context, although the API context also uses it in some cases: message response, form validation
|
||||
Locale translation.Locale
|
||||
}
|
||||
|
||||
func (b *Base) Deadline() (deadline time.Time, ok bool) {
|
||||
return b.originCtx.Deadline()
|
||||
}
|
||||
|
||||
func (b *Base) Done() <-chan struct{} {
|
||||
return b.originCtx.Done()
|
||||
}
|
||||
|
||||
func (b *Base) Err() error {
|
||||
return b.originCtx.Err()
|
||||
}
|
||||
|
||||
func (b *Base) Value(key any) any {
|
||||
for _, pair := range b.contextValues {
|
||||
if pair.key == key {
|
||||
return pair.valueFn()
|
||||
}
|
||||
}
|
||||
return b.originCtx.Value(key)
|
||||
}
|
||||
|
||||
func (b *Base) AppendContextValueFunc(key any, valueFn func() any) any {
|
||||
b.contextValues = append(b.contextValues, contextValuePair{key, valueFn})
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *Base) AppendContextValue(key, value any) any {
|
||||
b.contextValues = append(b.contextValues, contextValuePair{key, func() any { return value }})
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *Base) GetData() middleware.ContextData {
|
||||
return b.Data
|
||||
}
|
||||
|
||||
// AppendAccessControlExposeHeaders append headers by name to "Access-Control-Expose-Headers" header
|
||||
func (b *Base) AppendAccessControlExposeHeaders(names ...string) {
|
||||
val := b.RespHeader().Get("Access-Control-Expose-Headers")
|
||||
@@ -295,13 +255,6 @@ func (b *Base) ServeContent(r io.ReadSeeker, opts *ServeHeaderOptions) {
|
||||
http.ServeContent(b.Resp, b.Req, opts.Filename, opts.LastModified, r)
|
||||
}
|
||||
|
||||
// Close frees all resources hold by Context
|
||||
func (b *Base) cleanUp() {
|
||||
if b.Req != nil && b.Req.MultipartForm != nil {
|
||||
_ = b.Req.MultipartForm.RemoveAll() // remove the temp files buffered to tmp directory
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Base) Tr(msg string, args ...any) template.HTML {
|
||||
return b.Locale.Tr(msg, args...)
|
||||
}
|
||||
@@ -310,17 +263,28 @@ func (b *Base) TrN(cnt any, key1, keyN string, args ...any) template.HTML {
|
||||
return b.Locale.TrN(cnt, key1, keyN, args...)
|
||||
}
|
||||
|
||||
func NewBaseContext(resp http.ResponseWriter, req *http.Request) (b *Base, closeFunc func()) {
|
||||
b = &Base{
|
||||
originCtx: req.Context(),
|
||||
Req: req,
|
||||
Resp: WrapResponseWriter(resp),
|
||||
Locale: middleware.Locale(resp, req),
|
||||
Data: middleware.GetContextData(req.Context()),
|
||||
func NewBaseContext(resp http.ResponseWriter, req *http.Request) *Base {
|
||||
ds := reqctx.GetRequestDataStore(req.Context())
|
||||
b := &Base{
|
||||
Context: req.Context(),
|
||||
RequestDataStore: ds,
|
||||
Req: req,
|
||||
Resp: WrapResponseWriter(resp),
|
||||
Locale: middleware.Locale(resp, req),
|
||||
Data: ds.GetData(),
|
||||
}
|
||||
b.Req = b.Req.WithContext(b)
|
||||
b.AppendContextValue(BaseContextKey, b)
|
||||
b.AppendContextValue(translation.ContextKey, b.Locale)
|
||||
b.AppendContextValue(httplib.RequestContextKey, b.Req)
|
||||
return b, b.cleanUp
|
||||
ds.SetContextValue(BaseContextKey, b)
|
||||
ds.SetContextValue(translation.ContextKey, b.Locale)
|
||||
ds.SetContextValue(httplib.RequestContextKey, b.Req)
|
||||
return b
|
||||
}
|
||||
|
||||
func NewBaseContextForTest(resp http.ResponseWriter, req *http.Request) *Base {
|
||||
if !setting.IsInTesting {
|
||||
panic("This function is only for testing")
|
||||
}
|
||||
ctx := reqctx.NewRequestContextForTest(req.Context())
|
||||
*req = *req.WithContext(ctx)
|
||||
return NewBaseContext(resp, req)
|
||||
}
|
||||
|
@@ -14,6 +14,7 @@ import (
|
||||
)
|
||||
|
||||
func TestRedirect(t *testing.T) {
|
||||
setting.IsInTesting = true
|
||||
req, _ := http.NewRequest("GET", "/", nil)
|
||||
|
||||
cases := []struct {
|
||||
@@ -28,10 +29,9 @@ func TestRedirect(t *testing.T) {
|
||||
}
|
||||
for _, c := range cases {
|
||||
resp := httptest.NewRecorder()
|
||||
b, cleanup := NewBaseContext(resp, req)
|
||||
b := NewBaseContextForTest(resp, req)
|
||||
resp.Header().Add("Set-Cookie", (&http.Cookie{Name: setting.SessionConfig.CookieName, Value: "dummy"}).String())
|
||||
b.Redirect(c.url)
|
||||
cleanup()
|
||||
has := resp.Header().Get("Set-Cookie") == "i_like_gitea=dummy"
|
||||
assert.Equal(t, c.keep, has, "url = %q", c.url)
|
||||
}
|
||||
@@ -39,9 +39,8 @@ func TestRedirect(t *testing.T) {
|
||||
req, _ = http.NewRequest("GET", "/", nil)
|
||||
resp := httptest.NewRecorder()
|
||||
req.Header.Add("HX-Request", "true")
|
||||
b, cleanup := NewBaseContext(resp, req)
|
||||
b := NewBaseContextForTest(resp, req)
|
||||
b.Redirect("/other")
|
||||
cleanup()
|
||||
assert.Equal(t, "/other", resp.Header().Get("HX-Redirect"))
|
||||
assert.Equal(t, http.StatusNoContent, resp.Code)
|
||||
}
|
||||
|
@@ -18,7 +18,6 @@ import (
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/cache"
|
||||
"code.gitea.io/gitea/modules/gitrepo"
|
||||
"code.gitea.io/gitea/modules/httpcache"
|
||||
"code.gitea.io/gitea/modules/session"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
@@ -153,14 +152,9 @@ func Contexter() func(next http.Handler) http.Handler {
|
||||
}
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
||||
base, baseCleanUp := NewBaseContext(resp, req)
|
||||
defer baseCleanUp()
|
||||
base := NewBaseContext(resp, req)
|
||||
ctx := NewWebContext(base, rnd, session.GetContextSession(req))
|
||||
|
||||
ctx.Data.MergeFrom(middleware.CommonTemplateContextData())
|
||||
if setting.IsProd && !setting.IsInTesting {
|
||||
ctx.Data["Context"] = ctx // TODO: use "ctx" in template and remove this
|
||||
}
|
||||
ctx.Data["CurrentURL"] = setting.AppSubURL + req.URL.RequestURI()
|
||||
ctx.Data["Link"] = ctx.Link
|
||||
|
||||
@@ -168,9 +162,7 @@ func Contexter() func(next http.Handler) http.Handler {
|
||||
ctx.PageData = map[string]any{}
|
||||
ctx.Data["PageData"] = ctx.PageData
|
||||
|
||||
ctx.Base.AppendContextValue(WebContextKey, ctx)
|
||||
ctx.Base.AppendContextValueFunc(gitrepo.RepositoryContextKey, func() any { return ctx.Repo.GitRepo })
|
||||
|
||||
ctx.Base.SetContextValue(WebContextKey, ctx)
|
||||
ctx.Csrf = NewCSRFProtector(csrfOpts)
|
||||
|
||||
// Get the last flash message from cookie
|
||||
|
@@ -26,6 +26,7 @@ func TestRemoveSessionCookieHeader(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRedirectToCurrentSite(t *testing.T) {
|
||||
setting.IsInTesting = true
|
||||
defer test.MockVariableValue(&setting.AppURL, "http://localhost:3000/sub/")()
|
||||
defer test.MockVariableValue(&setting.AppSubURL, "/sub")()
|
||||
cases := []struct {
|
||||
@@ -40,8 +41,7 @@ func TestRedirectToCurrentSite(t *testing.T) {
|
||||
t.Run(c.location, func(t *testing.T) {
|
||||
req := &http.Request{URL: &url.URL{Path: "/"}}
|
||||
resp := httptest.NewRecorder()
|
||||
base, baseCleanUp := NewBaseContext(resp, req)
|
||||
defer baseCleanUp()
|
||||
base := NewBaseContextForTest(resp, req)
|
||||
ctx := NewWebContext(base, nil, nil)
|
||||
ctx.RedirectToCurrentSite(c.location)
|
||||
redirect := test.RedirectURL(resp)
|
||||
|
@@ -153,12 +153,10 @@ func PackageContexter() func(next http.Handler) http.Handler {
|
||||
renderer := templates.HTMLRenderer()
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
||||
base, baseCleanUp := NewBaseContext(resp, req)
|
||||
defer baseCleanUp()
|
||||
|
||||
base := NewBaseContext(resp, req)
|
||||
// it is still needed when rendering 500 page in a package handler
|
||||
ctx := NewWebContext(base, renderer, nil)
|
||||
ctx.Base.AppendContextValue(WebContextKey, ctx)
|
||||
ctx.SetContextValue(WebContextKey, ctx)
|
||||
next.ServeHTTP(ctx.Resp, ctx.Req)
|
||||
})
|
||||
}
|
||||
|
@@ -64,11 +64,9 @@ func GetPrivateContext(req *http.Request) *PrivateContext {
|
||||
func PrivateContexter() func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
base, baseCleanUp := NewBaseContext(w, req)
|
||||
base := NewBaseContext(w, req)
|
||||
ctx := &PrivateContext{Base: base}
|
||||
defer baseCleanUp()
|
||||
ctx.Base.AppendContextValue(privateContextKey, ctx)
|
||||
|
||||
ctx.SetContextValue(privateContextKey, ctx)
|
||||
next.ServeHTTP(ctx.Resp, ctx.Req)
|
||||
})
|
||||
}
|
||||
@@ -78,8 +76,15 @@ func PrivateContexter() func(http.Handler) http.Handler {
|
||||
// This function should be used when there is a need for work to continue even if the request has been cancelled.
|
||||
// Primarily this affects hook/post-receive and hook/proc-receive both of which need to continue working even if
|
||||
// the underlying request has timed out from the ssh/http push
|
||||
func OverrideContext(ctx *PrivateContext) (cancel context.CancelFunc) {
|
||||
// We now need to override the request context as the base for our work because even if the request is cancelled we have to continue this work
|
||||
ctx.Override, _, cancel = process.GetManager().AddTypedContext(graceful.GetManager().HammerContext(), fmt.Sprintf("PrivateContext: %s", ctx.Req.RequestURI), process.RequestProcessType, true)
|
||||
return cancel
|
||||
func OverrideContext() func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
// We now need to override the request context as the base for our work because even if the request is cancelled we have to continue this work
|
||||
ctx := GetPrivateContext(req)
|
||||
var finished func()
|
||||
ctx.Override, _, finished = process.GetManager().AddTypedContext(graceful.GetManager().HammerContext(), fmt.Sprintf("PrivateContext: %s", ctx.Req.RequestURI), process.RequestProcessType, true)
|
||||
defer finished()
|
||||
next.ServeHTTP(ctx.Resp, ctx.Req)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@@ -397,11 +397,13 @@ func repoAssignment(ctx *Context, repo *repo_model.Repository) {
|
||||
}
|
||||
|
||||
// RepoAssignment returns a middleware to handle repository assignment
|
||||
func RepoAssignment(ctx *Context) context.CancelFunc {
|
||||
func RepoAssignment(ctx *Context) {
|
||||
if _, repoAssignmentOnce := ctx.Data["repoAssignmentExecuted"]; repoAssignmentOnce {
|
||||
// FIXME: it should panic in dev/test modes to have a clear behavior
|
||||
log.Trace("RepoAssignment was exec already, skipping second call ...")
|
||||
return nil
|
||||
if !setting.IsProd || setting.IsInTesting {
|
||||
panic("RepoAssignment should not be executed twice")
|
||||
}
|
||||
return
|
||||
}
|
||||
ctx.Data["repoAssignmentExecuted"] = true
|
||||
|
||||
@@ -429,7 +431,7 @@ func RepoAssignment(ctx *Context) context.CancelFunc {
|
||||
// https://github.com/golang/go/issues/19760
|
||||
if ctx.FormString("go-get") == "1" {
|
||||
EarlyResponseForGoGetMeta(ctx)
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
if redirectUserID, err := user_model.LookupUserRedirect(ctx, userName); err == nil {
|
||||
@@ -442,7 +444,7 @@ func RepoAssignment(ctx *Context) context.CancelFunc {
|
||||
} else {
|
||||
ctx.ServerError("GetUserByName", err)
|
||||
}
|
||||
return nil
|
||||
return
|
||||
}
|
||||
}
|
||||
ctx.Repo.Owner = owner
|
||||
@@ -467,7 +469,7 @@ func RepoAssignment(ctx *Context) context.CancelFunc {
|
||||
redirectPath += "?" + ctx.Req.URL.RawQuery
|
||||
}
|
||||
ctx.Redirect(path.Join(setting.AppSubURL, redirectPath))
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
// Get repository.
|
||||
@@ -480,7 +482,7 @@ func RepoAssignment(ctx *Context) context.CancelFunc {
|
||||
} else if repo_model.IsErrRedirectNotExist(err) {
|
||||
if ctx.FormString("go-get") == "1" {
|
||||
EarlyResponseForGoGetMeta(ctx)
|
||||
return nil
|
||||
return
|
||||
}
|
||||
ctx.NotFound("GetRepositoryByName", nil)
|
||||
} else {
|
||||
@@ -489,13 +491,13 @@ func RepoAssignment(ctx *Context) context.CancelFunc {
|
||||
} else {
|
||||
ctx.ServerError("GetRepositoryByName", err)
|
||||
}
|
||||
return nil
|
||||
return
|
||||
}
|
||||
repo.Owner = owner
|
||||
|
||||
repoAssignment(ctx, repo)
|
||||
if ctx.Written() {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Repo.RepoLink = repo.Link()
|
||||
@@ -520,7 +522,7 @@ func RepoAssignment(ctx *Context) context.CancelFunc {
|
||||
})
|
||||
if err != nil {
|
||||
ctx.ServerError("GetReleaseCountByRepoID", err)
|
||||
return nil
|
||||
return
|
||||
}
|
||||
ctx.Data["NumReleases"], err = db.Count[repo_model.Release](ctx, repo_model.FindReleasesOptions{
|
||||
// only show draft releases for users who can write, read-only users shouldn't see draft releases.
|
||||
@@ -529,7 +531,7 @@ func RepoAssignment(ctx *Context) context.CancelFunc {
|
||||
})
|
||||
if err != nil {
|
||||
ctx.ServerError("GetReleaseCountByRepoID", err)
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Data["Title"] = owner.Name + "/" + repo.Name
|
||||
@@ -546,14 +548,14 @@ func RepoAssignment(ctx *Context) context.CancelFunc {
|
||||
canSignedUserFork, err := repo_module.CanUserForkRepo(ctx, ctx.Doer, ctx.Repo.Repository)
|
||||
if err != nil {
|
||||
ctx.ServerError("CanUserForkRepo", err)
|
||||
return nil
|
||||
return
|
||||
}
|
||||
ctx.Data["CanSignedUserFork"] = canSignedUserFork
|
||||
|
||||
userAndOrgForks, err := repo_model.GetForksByUserAndOrgs(ctx, ctx.Doer, ctx.Repo.Repository)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetForksByUserAndOrgs", err)
|
||||
return nil
|
||||
return
|
||||
}
|
||||
ctx.Data["UserAndOrgForks"] = userAndOrgForks
|
||||
|
||||
@@ -587,14 +589,14 @@ func RepoAssignment(ctx *Context) context.CancelFunc {
|
||||
if repo.IsFork {
|
||||
RetrieveBaseRepo(ctx, repo)
|
||||
if ctx.Written() {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if repo.IsGenerated() {
|
||||
RetrieveTemplateRepo(ctx, repo)
|
||||
if ctx.Written() {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -609,10 +611,18 @@ func RepoAssignment(ctx *Context) context.CancelFunc {
|
||||
if !isHomeOrSettings {
|
||||
ctx.Redirect(ctx.Repo.RepoLink)
|
||||
}
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
gitRepo, err := gitrepo.OpenRepository(ctx, repo)
|
||||
if ctx.Repo.GitRepo != nil {
|
||||
if !setting.IsProd || setting.IsInTesting {
|
||||
panic("RepoAssignment: GitRepo should be nil")
|
||||
}
|
||||
_ = ctx.Repo.GitRepo.Close()
|
||||
ctx.Repo.GitRepo = nil
|
||||
}
|
||||
|
||||
ctx.Repo.GitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, 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)
|
||||
@@ -622,28 +632,16 @@ func RepoAssignment(ctx *Context) context.CancelFunc {
|
||||
if !isHomeOrSettings {
|
||||
ctx.Redirect(ctx.Repo.RepoLink)
|
||||
}
|
||||
return nil
|
||||
return
|
||||
}
|
||||
ctx.ServerError("RepoAssignment Invalid repo "+repo.FullName(), err)
|
||||
return nil
|
||||
}
|
||||
if ctx.Repo.GitRepo != nil {
|
||||
ctx.Repo.GitRepo.Close()
|
||||
}
|
||||
ctx.Repo.GitRepo = gitRepo
|
||||
|
||||
// We opened it, we should close it
|
||||
cancel := func() {
|
||||
// If it's been set to nil then assume someone else has closed it.
|
||||
if ctx.Repo.GitRepo != nil {
|
||||
ctx.Repo.GitRepo.Close()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Stop at this point when the repo is empty.
|
||||
if ctx.Repo.Repository.IsEmpty {
|
||||
ctx.Data["BranchName"] = ctx.Repo.Repository.DefaultBranch
|
||||
return cancel
|
||||
return
|
||||
}
|
||||
|
||||
branchOpts := git_model.FindBranchOptions{
|
||||
@@ -654,7 +652,7 @@ func RepoAssignment(ctx *Context) context.CancelFunc {
|
||||
branchesTotal, err := db.Count[git_model.Branch](ctx, branchOpts)
|
||||
if err != nil {
|
||||
ctx.ServerError("CountBranches", err)
|
||||
return cancel
|
||||
return
|
||||
}
|
||||
|
||||
// non-empty repo should have at least 1 branch, so this repository's branches haven't been synced yet
|
||||
@@ -662,7 +660,7 @@ func RepoAssignment(ctx *Context) context.CancelFunc {
|
||||
branchesTotal, err = repo_module.SyncRepoBranches(ctx, ctx.Repo.Repository.ID, 0)
|
||||
if err != nil {
|
||||
ctx.ServerError("SyncRepoBranches", err)
|
||||
return cancel
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -670,7 +668,7 @@ func RepoAssignment(ctx *Context) context.CancelFunc {
|
||||
|
||||
// If no branch is set in the request URL, try to guess a default one.
|
||||
if len(ctx.Repo.BranchName) == 0 {
|
||||
if len(ctx.Repo.Repository.DefaultBranch) > 0 && gitRepo.IsBranchExist(ctx.Repo.Repository.DefaultBranch) {
|
||||
if len(ctx.Repo.Repository.DefaultBranch) > 0 && ctx.Repo.GitRepo.IsBranchExist(ctx.Repo.Repository.DefaultBranch) {
|
||||
ctx.Repo.BranchName = ctx.Repo.Repository.DefaultBranch
|
||||
} else {
|
||||
ctx.Repo.BranchName, _ = gitrepo.GetDefaultBranch(ctx, ctx.Repo.Repository)
|
||||
@@ -711,12 +709,12 @@ func RepoAssignment(ctx *Context) context.CancelFunc {
|
||||
repoTransfer, err := repo_model.GetPendingRepositoryTransfer(ctx, ctx.Repo.Repository)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetPendingRepositoryTransfer", err)
|
||||
return cancel
|
||||
return
|
||||
}
|
||||
|
||||
if err := repoTransfer.LoadAttributes(ctx); err != nil {
|
||||
ctx.ServerError("LoadRecipient", err)
|
||||
return cancel
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Data["RepoTransfer"] = repoTransfer
|
||||
@@ -731,7 +729,6 @@ func RepoAssignment(ctx *Context) context.CancelFunc {
|
||||
ctx.Data["GoDocDirectory"] = fullURLPrefix + "{/dir}"
|
||||
ctx.Data["GoDocFile"] = fullURLPrefix + "{/dir}/{file}#L{line}"
|
||||
}
|
||||
return cancel
|
||||
}
|
||||
|
||||
// RepoRefType type of repo reference
|
||||
@@ -750,7 +747,7 @@ const headRefName = "HEAD"
|
||||
|
||||
// RepoRef handles repository reference names when the ref name is not
|
||||
// explicitly given
|
||||
func RepoRef() func(*Context) context.CancelFunc {
|
||||
func RepoRef() func(*Context) {
|
||||
// since no ref name is explicitly specified, ok to just use branch
|
||||
return RepoRefByType(RepoRefBranch)
|
||||
}
|
||||
@@ -865,9 +862,9 @@ type RepoRefByTypeOptions struct {
|
||||
|
||||
// RepoRefByType handles repository reference name for a specific type
|
||||
// of repository reference
|
||||
func RepoRefByType(detectRefType RepoRefType, opts ...RepoRefByTypeOptions) func(*Context) context.CancelFunc {
|
||||
func RepoRefByType(detectRefType RepoRefType, opts ...RepoRefByTypeOptions) func(*Context) {
|
||||
opt := util.OptionalArg(opts)
|
||||
return func(ctx *Context) (cancel context.CancelFunc) {
|
||||
return func(ctx *Context) {
|
||||
refType := detectRefType
|
||||
// Empty repository does not have reference information.
|
||||
if ctx.Repo.Repository.IsEmpty {
|
||||
@@ -875,7 +872,7 @@ func RepoRefByType(detectRefType RepoRefType, opts ...RepoRefByTypeOptions) func
|
||||
ctx.Repo.IsViewBranch = true
|
||||
ctx.Repo.BranchName = ctx.Repo.Repository.DefaultBranch
|
||||
ctx.Data["TreePath"] = ""
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -884,17 +881,10 @@ func RepoRefByType(detectRefType RepoRefType, opts ...RepoRefByTypeOptions) func
|
||||
)
|
||||
|
||||
if ctx.Repo.GitRepo == nil {
|
||||
ctx.Repo.GitRepo, err = gitrepo.OpenRepository(ctx, ctx.Repo.Repository)
|
||||
ctx.Repo.GitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, ctx, ctx.Repo.Repository)
|
||||
if err != nil {
|
||||
ctx.ServerError(fmt.Sprintf("Open Repository %v failed", ctx.Repo.Repository.FullName()), err)
|
||||
return nil
|
||||
}
|
||||
// We opened it, we should close it
|
||||
cancel = func() {
|
||||
// If it's been set to nil then assume someone else has closed it.
|
||||
if ctx.Repo.GitRepo != nil {
|
||||
ctx.Repo.GitRepo.Close()
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -924,7 +914,7 @@ func RepoRefByType(detectRefType RepoRefType, opts ...RepoRefByTypeOptions) func
|
||||
ctx.Repo.Repository.MarkAsBrokenEmpty()
|
||||
} else {
|
||||
ctx.ServerError("GetBranchCommit", err)
|
||||
return cancel
|
||||
return
|
||||
}
|
||||
ctx.Repo.IsViewBranch = true
|
||||
} else {
|
||||
@@ -941,7 +931,7 @@ func RepoRefByType(detectRefType RepoRefType, opts ...RepoRefByTypeOptions) func
|
||||
ctx.Flash.Info(ctx.Tr("repo.branch.renamed", refName, renamedBranchName))
|
||||
link := setting.AppSubURL + strings.Replace(ctx.Req.URL.EscapedPath(), util.PathEscapeSegments(refName), util.PathEscapeSegments(renamedBranchName), 1)
|
||||
ctx.Redirect(link)
|
||||
return cancel
|
||||
return
|
||||
}
|
||||
|
||||
if refType == RepoRefBranch && ctx.Repo.GitRepo.IsBranchExist(refName) {
|
||||
@@ -951,7 +941,7 @@ func RepoRefByType(detectRefType RepoRefType, opts ...RepoRefByTypeOptions) func
|
||||
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetBranchCommit", err)
|
||||
return cancel
|
||||
return
|
||||
}
|
||||
ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
|
||||
} else if refType == RepoRefTag && ctx.Repo.GitRepo.IsTagExist(refName) {
|
||||
@@ -962,10 +952,10 @@ func RepoRefByType(detectRefType RepoRefType, opts ...RepoRefByTypeOptions) func
|
||||
if err != nil {
|
||||
if git.IsErrNotExist(err) {
|
||||
ctx.NotFound("GetTagCommit", err)
|
||||
return cancel
|
||||
return
|
||||
}
|
||||
ctx.ServerError("GetTagCommit", err)
|
||||
return cancel
|
||||
return
|
||||
}
|
||||
ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
|
||||
} else if git.IsStringLikelyCommitID(ctx.Repo.GetObjectFormat(), refName, 7) {
|
||||
@@ -975,7 +965,7 @@ func RepoRefByType(detectRefType RepoRefType, opts ...RepoRefByTypeOptions) func
|
||||
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetCommit(refName)
|
||||
if err != nil {
|
||||
ctx.NotFound("GetCommit", err)
|
||||
return cancel
|
||||
return
|
||||
}
|
||||
// If short commit ID add canonical link header
|
||||
if len(refName) < ctx.Repo.GetObjectFormat().FullLength() {
|
||||
@@ -984,10 +974,10 @@ func RepoRefByType(detectRefType RepoRefType, opts ...RepoRefByTypeOptions) func
|
||||
}
|
||||
} else {
|
||||
if opt.IgnoreNotExistErr {
|
||||
return cancel
|
||||
return
|
||||
}
|
||||
ctx.NotFound("RepoRef invalid repo", fmt.Errorf("branch or tag not exist: %s", refName))
|
||||
return cancel
|
||||
return
|
||||
}
|
||||
|
||||
if guessLegacyPath {
|
||||
@@ -999,7 +989,7 @@ func RepoRefByType(detectRefType RepoRefType, opts ...RepoRefByTypeOptions) func
|
||||
ctx.Repo.BranchNameSubURL(),
|
||||
util.PathEscapeSegments(ctx.Repo.TreePath))
|
||||
ctx.Redirect(redirect)
|
||||
return cancel
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1017,12 +1007,10 @@ func RepoRefByType(detectRefType RepoRefType, opts ...RepoRefByTypeOptions) func
|
||||
ctx.Repo.CommitsCount, err = ctx.Repo.GetCommitsCount()
|
||||
if err != nil {
|
||||
ctx.ServerError("GetCommitsCount", err)
|
||||
return cancel
|
||||
return
|
||||
}
|
||||
ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount
|
||||
ctx.Repo.GitRepo.LastCommitCache = git.NewLastCommitCache(ctx.Repo.CommitsCount, ctx.Repo.Repository.FullName(), ctx.Repo.GitRepo, cache.GetCache())
|
||||
|
||||
return cancel
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user