From 8640c3d5abd9fc51877a232c6eb1e9797307fd54 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 26 Sep 2018 20:24:25 +0800 Subject: [PATCH 1/8] improve performance of dashboard --- models/action.go | 47 ++++++++++++++++----- models/issue.go | 33 ++++++++++++--- models/issue_comment.go | 62 ++++++++++++++++++---------- models/issue_comment_list.go | 53 ++++++++++++++++++++++++ models/update.go | 2 +- routers/api/v1/repo/issue_comment.go | 2 +- routers/repo/issue.go | 16 +++++++ routers/repo/pull.go | 32 ++++++-------- routers/user/home.go | 1 + 9 files changed, 189 insertions(+), 59 deletions(-) create mode 100644 models/issue_comment_list.go diff --git a/models/action.go b/models/action.go index 9050be22a640..eb7f694bcf9f 100644 --- a/models/action.go +++ b/models/action.go @@ -330,13 +330,15 @@ type PushCommits struct { Commits []*PushCommit CompareURL string - avatars map[string]string + avatars map[string]string + emailUsers map[string]*User } // NewPushCommits creates a new PushCommits object. func NewPushCommits() *PushCommits { return &PushCommits{ - avatars: make(map[string]string), + avatars: make(map[string]string), + emailUsers: make(map[string]*User), } } @@ -344,16 +346,31 @@ func NewPushCommits() *PushCommits { // api.PayloadCommit format. func (pc *PushCommits) ToAPIPayloadCommits(repoLink string) []*api.PayloadCommit { commits := make([]*api.PayloadCommit, len(pc.Commits)) + + var err error for i, commit := range pc.Commits { authorUsername := "" - author, err := GetUserByEmail(commit.AuthorEmail) - if err == nil { + author, ok := pc.emailUsers[commit.AuthorEmail] + if !ok { + author, err = GetUserByEmail(commit.AuthorEmail) + if err == nil { + authorUsername = author.Name + pc.emailUsers[commit.AuthorEmail] = author + } + } else { authorUsername = author.Name } + committerUsername := "" - committer, err := GetUserByEmail(commit.CommitterEmail) - if err == nil { - // TODO: check errors other than email not found. + committer, ok := pc.emailUsers[commit.CommitterEmail] + if !ok { + committer, err = GetUserByEmail(commit.CommitterEmail) + if err == nil { + // TODO: check errors other than email not found. + committerUsername = committer.Name + pc.emailUsers[commit.CommitterEmail] = committer + } + } else { committerUsername = committer.Name } commits[i] = &api.PayloadCommit{ @@ -379,18 +396,28 @@ func (pc *PushCommits) ToAPIPayloadCommits(repoLink string) []*api.PayloadCommit // AvatarLink tries to match user in database with e-mail // in order to show custom avatar, and falls back to general avatar link. func (pc *PushCommits) AvatarLink(email string) string { - _, ok := pc.avatars[email] + avatar, ok := pc.avatars[email] + if ok { + return avatar + } + + u, ok := pc.emailUsers[email] if !ok { - u, err := GetUserByEmail(email) + var err error + u, err = GetUserByEmail(email) if err != nil { pc.avatars[email] = base.AvatarLink(email) if !IsErrUserNotExist(err) { log.Error(4, "GetUserByEmail: %v", err) + return "" } } else { - pc.avatars[email] = u.RelAvatarLink() + pc.emailUsers[email] = u } } + if u != nil { + pc.avatars[email] = u.RelAvatarLink() + } return pc.avatars[email] } diff --git a/models/issue.go b/models/issue.go index d4222b04a056..0d3ea46e9c64 100644 --- a/models/issue.go +++ b/models/issue.go @@ -129,6 +129,11 @@ func (issue *Issue) loadLabels(e Engine) (err error) { return nil } +// LoadPoster loads poster +func (issue *Issue) LoadPoster() error { + return issue.loadPoster(x) +} + func (issue *Issue) loadPoster(e Engine) (err error) { if issue.Poster == nil { issue.Poster, err = getUserByID(e, issue.PosterID) @@ -154,10 +159,16 @@ func (issue *Issue) loadPullRequest(e Engine) (err error) { } return fmt.Errorf("getPullRequestByIssueID [%d]: %v", issue.ID, err) } + issue.PullRequest.Issue = issue } return nil } +// LoadPullRequest loads pull request info +func (issue *Issue) LoadPullRequest() error { + return issue.loadPullRequest(x) +} + func (issue *Issue) loadComments(e Engine) (err error) { if issue.Comments != nil { return nil @@ -279,6 +290,7 @@ func (issue *Issue) HTMLURL() string { } else { path = "issues" } + issue.loadRepo(x) return fmt.Sprintf("%s/%s/%d", issue.Repo.HTMLURL(), path, issue.Index) } @@ -1095,8 +1107,8 @@ func NewIssue(repo *Repository, issue *Issue, labelIDs []int64, assigneeIDs []in return nil } -// GetRawIssueByIndex returns raw issue without loading attributes by index in a repository. -func GetRawIssueByIndex(repoID, index int64) (*Issue, error) { +// GetIssueByIndex returns raw issue without loading attributes by index in a repository. +func GetIssueByIndex(repoID, index int64) (*Issue, error) { issue := &Issue{ RepoID: repoID, Index: index, @@ -1110,9 +1122,9 @@ func GetRawIssueByIndex(repoID, index int64) (*Issue, error) { return issue, nil } -// GetIssueByIndex returns issue by index in a repository. -func GetIssueByIndex(repoID, index int64) (*Issue, error) { - issue, err := GetRawIssueByIndex(repoID, index) +// GetIssueWithAttrsByIndex returns issue by index in a repository. +func GetIssueWithAttrsByIndex(repoID, index int64) (*Issue, error) { + issue, err := GetIssueByIndex(repoID, index) if err != nil { return nil, err } @@ -1127,7 +1139,16 @@ func getIssueByID(e Engine, id int64) (*Issue, error) { } else if !has { return nil, ErrIssueNotExist{id, 0, 0} } - return issue, issue.loadAttributes(e) + return issue, nil +} + +// GetIssueWithAttrsByID returns an issue with attributes by given ID. +func GetIssueWithAttrsByID(id int64) (*Issue, error) { + issue, err := getIssueByID(x, id) + if err != nil { + return nil, err + } + return issue, issue.loadAttributes(x) } // GetIssueByID returns an issue by given ID. diff --git a/models/issue_comment.go b/models/issue_comment.go index 96b162ca196b..5e9b10db4ac2 100644 --- a/models/issue_comment.go +++ b/models/issue_comment.go @@ -150,25 +150,6 @@ func (c *Comment) LoadIssue() (err error) { return } -// AfterLoad is invoked from XORM after setting the values of all fields of this object. -func (c *Comment) AfterLoad(session *xorm.Session) { - var err error - c.Attachments, err = getAttachmentsByCommentID(session, c.ID) - if err != nil { - log.Error(3, "getAttachmentsByCommentID[%d]: %v", c.ID, err) - } - - c.Poster, err = getUserByID(session, c.PosterID) - if err != nil { - if IsErrUserNotExist(err) { - c.PosterID = -1 - c.Poster = NewGhostUser() - } else { - log.Error(3, "getUserByID[%d]: %v", c.ID, err) - } - } -} - // AfterDelete is invoked from XORM after the object is deleted. func (c *Comment) AfterDelete() { if c.ID <= 0 { @@ -303,6 +284,39 @@ func (c *Comment) LoadMilestone() error { return nil } +// LoadPoster loads comment poster +func (c *Comment) LoadPoster() error { + if c.PosterID <= 0 || c.Poster != nil { + return nil + } + + var err error + c.Poster, err = getUserByID(x, c.PosterID) + if err != nil { + if IsErrUserNotExist(err) { + c.PosterID = -1 + c.Poster = NewGhostUser() + } else { + log.Error(3, "getUserByID[%d]: %v", c.ID, err) + } + } + return nil +} + +// LoadAttachments loads attachments +func (c *Comment) LoadAttachments() error { + if len(c.Attachments) > 0 { + return nil + } + + var err error + c.Attachments, err = getAttachmentsByCommentID(x, c.ID) + if err != nil { + log.Error(3, "getAttachmentsByCommentID[%d]: %v", c.ID, err) + } + return nil +} + // LoadAssigneeUser if comment.Type is CommentTypeAssignees, then load assignees func (c *Comment) LoadAssigneeUser() error { var err error @@ -375,8 +389,10 @@ func (c *Comment) LoadReactions() error { } func (c *Comment) loadReview(e Engine) (err error) { - if c.Review, err = getReviewByID(e, c.ReviewID); err != nil { - return err + if c.Review == nil { + if c.Review, err = getReviewByID(e, c.ReviewID); err != nil { + return err + } } return nil } @@ -1095,6 +1111,10 @@ func fetchCodeCommentsByReview(e Engine, issue *Issue, currentUser *User, review return nil, err } + if err := CommentList(comments).loadPosters(e); err != nil { + return nil, err + } + if err := issue.loadRepo(e); err != nil { return nil, err } diff --git a/models/issue_comment_list.go b/models/issue_comment_list.go new file mode 100644 index 000000000000..32ed0bd63eb7 --- /dev/null +++ b/models/issue_comment_list.go @@ -0,0 +1,53 @@ +// Copyright 2018 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package models + +// CommentList defines a list of comments +type CommentList []*Comment + +func (comments CommentList) getPosterIDs() []int64 { + commentIDs := make(map[int64]struct{}, len(comments)) + for _, comment := range comments { + if _, ok := commentIDs[comment.PosterID]; !ok { + commentIDs[comment.PosterID] = struct{}{} + } + } + return keysInt64(commentIDs) +} + +func (comments CommentList) loadPosters(e Engine) error { + if len(comments) == 0 { + return nil + } + + posterIDs := comments.getPosterIDs() + posterMaps := make(map[int64]*User, len(posterIDs)) + var left = len(posterIDs) + for left > 0 { + var limit = defaultMaxInSize + if left < limit { + limit = left + } + err := e. + In("id", posterIDs[:limit]). + Find(&posterMaps) + if err != nil { + return err + } + left = left - limit + posterIDs = posterIDs[limit:] + } + + for _, comment := range comments { + if comment.PosterID <= 0 { + continue + } + var ok bool + if comment.Poster, ok = posterMaps[comment.PosterID]; !ok { + comment.Poster = NewGhostUser() + } + } + return nil +} diff --git a/models/update.go b/models/update.go index 0f71cd1e709a..9cd4d0d97773 100644 --- a/models/update.go +++ b/models/update.go @@ -51,7 +51,7 @@ func ListToPushCommits(l *list.List) *PushCommits { } commits = append(commits, CommitToPushCommit(commit)) } - return &PushCommits{l.Len(), commits, "", nil} + return &PushCommits{l.Len(), commits, "", make(map[string]string), make(map[string]*User)} } // PushUpdateOptions defines the push update options diff --git a/routers/api/v1/repo/issue_comment.go b/routers/api/v1/repo/issue_comment.go index 3af0290585f9..85b599e9f7f2 100644 --- a/routers/api/v1/repo/issue_comment.go +++ b/routers/api/v1/repo/issue_comment.go @@ -51,7 +51,7 @@ func ListIssueComments(ctx *context.APIContext) { } // comments,err:=models.GetCommentsByIssueIDSince(, since) - issue, err := models.GetRawIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) + issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) if err != nil { ctx.Error(500, "GetRawIssueByIndex", err) return diff --git a/routers/repo/issue.go b/routers/repo/issue.go index 34a01617e4da..78be178f4105 100644 --- a/routers/repo/issue.go +++ b/routers/repo/issue.go @@ -576,6 +576,12 @@ func ViewIssue(ctx *context.Context) { ctx.Data["RequireTribute"] = true renderAttachmentSettings(ctx) + err = issue.LoadAttributes() + if err != nil { + ctx.ServerError("GetIssueByIndex", err) + return + } + ctx.Data["Title"] = fmt.Sprintf("#%d - %s", issue.Index, issue.Title) var iw *models.IssueWatch @@ -697,7 +703,17 @@ func ViewIssue(ctx *context.Context) { // Render comments and and fetch participants. participants[0] = issue.Poster for _, comment = range issue.Comments { + if err := comment.LoadPoster(); err != nil { + ctx.ServerError("LoadPoster", err) + return + } + if comment.Type == models.CommentTypeComment { + if err := comment.LoadAttachments(); err != nil { + ctx.ServerError("LoadAttachments", err) + return + } + comment.RenderedContent = string(markdown.Render([]byte(comment.Content), ctx.Repo.RepoLink, ctx.Repo.Repository.ComposeMetas())) diff --git a/routers/repo/pull.go b/routers/repo/pull.go index 4adfb96e748b..f4c7fcfe7d64 100644 --- a/routers/repo/pull.go +++ b/routers/repo/pull.go @@ -223,6 +223,10 @@ func checkPullInfo(ctx *context.Context) *models.Issue { } return nil } + if err = issue.LoadPoster(); err != nil { + ctx.ServerError("LoadPoster", err) + return nil + } ctx.Data["Title"] = fmt.Sprintf("#%d - %s", issue.Index, issue.Title) ctx.Data["Issue"] = issue @@ -231,6 +235,11 @@ func checkPullInfo(ctx *context.Context) *models.Issue { return nil } + if err = issue.LoadPullRequest(); err != nil { + ctx.ServerError("LoadPullRequest", err) + return nil + } + if err = issue.PullRequest.GetHeadRepo(); err != nil { ctx.ServerError("GetHeadRepo", err) return nil @@ -519,16 +528,7 @@ func MergePullRequest(ctx *context.Context, form auth.MergePullRequestForm) { return } - pr, err := models.GetPullRequestByIssueID(issue.ID) - if err != nil { - if models.IsErrPullRequestNotExist(err) { - ctx.NotFound("GetPullRequestByIssueID", nil) - } else { - ctx.ServerError("GetPullRequestByIssueID", err) - } - return - } - pr.Issue = issue + pr := issue.PullRequest if !pr.CanAutoMerge() || pr.HasMerged { ctx.NotFound("MergePullRequest", nil) @@ -949,15 +949,7 @@ func CleanUpPullRequest(ctx *context.Context) { return } - pr, err := models.GetPullRequestByIssueID(issue.ID) - if err != nil { - if models.IsErrPullRequestNotExist(err) { - ctx.NotFound("GetPullRequestByIssueID", nil) - } else { - ctx.ServerError("GetPullRequestByIssueID", err) - } - return - } + pr := issue.PullRequest // Allow cleanup only for merged PR if !pr.HasMerged { @@ -965,7 +957,7 @@ func CleanUpPullRequest(ctx *context.Context) { return } - if err = pr.GetHeadRepo(); err != nil { + if err := pr.GetHeadRepo(); err != nil { ctx.ServerError("GetHeadRepo", err) return } else if pr.HeadRepo == nil { diff --git a/routers/user/home.go b/routers/user/home.go index 4c6a9f6cd095..883adf4e6cfb 100644 --- a/routers/user/home.go +++ b/routers/user/home.go @@ -138,6 +138,7 @@ func Dashboard(ctx *context.Context) { OnlyPerformedBy: false, IncludeDeleted: false, }) + if ctx.Written() { return } From e534075a93ca37ac083dfa1663d8efbfeacf4b41 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 26 Sep 2018 22:08:52 +0800 Subject: [PATCH 2/8] fix bug --- models/action.go | 6 ++++-- models/issue.go | 22 +++++++++++++--------- models/issue_comment.go | 4 ++-- models/pull.go | 2 +- routers/api/v1/repo/issue.go | 6 ++++-- routers/api/v1/repo/pull.go | 4 ++-- routers/repo/issue.go | 5 +++-- 7 files changed, 29 insertions(+), 20 deletions(-) diff --git a/models/action.go b/models/action.go index eb7f694bcf9f..5ba74e7e7baf 100644 --- a/models/action.go +++ b/models/action.go @@ -506,7 +506,8 @@ func UpdateIssuesCommit(doer *User, repo *Repository, commits []*PushCommit) err continue } - if err = issue.ChangeStatus(doer, repo, true); err != nil { + issue.Repo = repo + if err = issue.ChangeStatus(doer, true); err != nil { // Don't return an error when dependencies are open as this would let the push fail if IsErrDependenciesLeft(err) { return nil @@ -531,7 +532,8 @@ func UpdateIssuesCommit(doer *User, repo *Repository, commits []*PushCommit) err continue } - if err = issue.ChangeStatus(doer, repo, false); err != nil { + issue.Repo = repo + if err = issue.ChangeStatus(doer, false); err != nil { return err } } diff --git a/models/issue.go b/models/issue.go index 0d3ea46e9c64..b80ca47f8d66 100644 --- a/models/issue.go +++ b/models/issue.go @@ -664,7 +664,7 @@ func UpdateIssueCols(issue *Issue, cols ...string) error { return updateIssueCols(x, issue, cols...) } -func (issue *Issue) changeStatus(e *xorm.Session, doer *User, repo *Repository, isClosed bool) (err error) { +func (issue *Issue) changeStatus(e *xorm.Session, doer *User, isClosed bool) (err error) { // Nothing should be performed if current status is same as target status if issue.IsClosed == isClosed { return nil @@ -715,7 +715,7 @@ func (issue *Issue) changeStatus(e *xorm.Session, doer *User, repo *Repository, } // New action comment - if _, err = createStatusComment(e, doer, repo, issue); err != nil { + if _, err = createStatusComment(e, doer, issue); err != nil { return err } @@ -723,14 +723,18 @@ func (issue *Issue) changeStatus(e *xorm.Session, doer *User, repo *Repository, } // ChangeStatus changes issue status to open or closed. -func (issue *Issue) ChangeStatus(doer *User, repo *Repository, isClosed bool) (err error) { +func (issue *Issue) ChangeStatus(doer *User, isClosed bool) (err error) { sess := x.NewSession() defer sess.Close() if err = sess.Begin(); err != nil { return err } - if err = issue.changeStatus(sess, doer, repo, isClosed); err != nil { + if err = issue.loadRepo(sess); err != nil { + return err + } + + if err = issue.changeStatus(sess, doer, isClosed); err != nil { return err } @@ -746,7 +750,7 @@ func (issue *Issue) ChangeStatus(doer *User, repo *Repository, isClosed bool) (e apiPullRequest := &api.PullRequestPayload{ Index: issue.Index, PullRequest: issue.PullRequest.APIFormat(), - Repository: repo.APIFormat(mode), + Repository: issue.Repo.APIFormat(mode), Sender: doer.APIFormat(), } if isClosed { @@ -754,12 +758,12 @@ func (issue *Issue) ChangeStatus(doer *User, repo *Repository, isClosed bool) (e } else { apiPullRequest.Action = api.HookIssueReOpened } - err = PrepareWebhooks(repo, HookEventPullRequest, apiPullRequest) + err = PrepareWebhooks(issue.Repo, HookEventPullRequest, apiPullRequest) } else { apiIssue := &api.IssuePayload{ Index: issue.Index, Issue: issue.APIFormat(), - Repository: repo.APIFormat(mode), + Repository: issue.Repo.APIFormat(mode), Sender: doer.APIFormat(), } if isClosed { @@ -767,12 +771,12 @@ func (issue *Issue) ChangeStatus(doer *User, repo *Repository, isClosed bool) (e } else { apiIssue.Action = api.HookIssueReOpened } - err = PrepareWebhooks(repo, HookEventIssues, apiIssue) + err = PrepareWebhooks(issue.Repo, HookEventIssues, apiIssue) } if err != nil { log.Error(4, "PrepareWebhooks [is_pull: %v, is_closed: %v]: %v", issue.IsPull, isClosed, err) } else { - go HookQueue.Add(repo.ID) + go HookQueue.Add(issue.Repo.ID) } return nil diff --git a/models/issue_comment.go b/models/issue_comment.go index 5e9b10db4ac2..74215ff20bdc 100644 --- a/models/issue_comment.go +++ b/models/issue_comment.go @@ -617,7 +617,7 @@ func sendCreateCommentAction(e *xorm.Session, opts *CreateCommentOptions, commen return nil } -func createStatusComment(e *xorm.Session, doer *User, repo *Repository, issue *Issue) (*Comment, error) { +func createStatusComment(e *xorm.Session, doer *User, issue *Issue) (*Comment, error) { cmtType := CommentTypeClose if !issue.IsClosed { cmtType = CommentTypeReopen @@ -625,7 +625,7 @@ func createStatusComment(e *xorm.Session, doer *User, repo *Repository, issue *I return createComment(e, &CreateCommentOptions{ Type: cmtType, Doer: doer, - Repo: repo, + Repo: issue.Repo, Issue: issue, }) } diff --git a/models/pull.go b/models/pull.go index e97faa8f519d..15a90f92cbcc 100644 --- a/models/pull.go +++ b/models/pull.go @@ -535,7 +535,7 @@ func (pr *PullRequest) setMerged() (err error) { return err } - if err = pr.Issue.changeStatus(sess, pr.Merger, pr.Issue.Repo, true); err != nil { + if err = pr.Issue.changeStatus(sess, pr.Merger, true); err != nil { return fmt.Errorf("Issue.changeStatus: %v", err) } if _, err = sess.ID(pr.ID).Cols("has_merged, status, merged_commit_id, merger_id, merged_unix").Update(pr); err != nil { diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go index 7a8ed09b4864..65c97888f599 100644 --- a/routers/api/v1/repo/issue.go +++ b/routers/api/v1/repo/issue.go @@ -175,6 +175,7 @@ func CreateIssue(ctx *context.APIContext, form api.CreateIssueOption) { issue := &models.Issue{ RepoID: ctx.Repo.Repository.ID, + Repo: ctx.Repo.Repository, Title: form.Title, PosterID: ctx.User.ID, Poster: ctx.User, @@ -212,7 +213,7 @@ func CreateIssue(ctx *context.APIContext, form api.CreateIssueOption) { notification.NotifyNewIssue(issue) if form.Closed { - if err := issue.ChangeStatus(ctx.User, ctx.Repo.Repository, true); err != nil { + if err := issue.ChangeStatus(ctx.User, true); err != nil { if models.IsErrDependenciesLeft(err) { ctx.Error(http.StatusPreconditionFailed, "DependenciesLeft", "cannot close this issue because it still has open dependencies") return @@ -273,6 +274,7 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) { } return } + issue.Repo = ctx.Repo.Repository if !issue.IsPoster(ctx.User.ID) && !ctx.Repo.CanWrite(models.UnitTypeIssues) { ctx.Status(403) @@ -333,7 +335,7 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) { return } if form.State != nil { - if err = issue.ChangeStatus(ctx.User, ctx.Repo.Repository, api.StateClosed == api.StateType(*form.State)); err != nil { + if err = issue.ChangeStatus(ctx.User, api.StateClosed == api.StateType(*form.State)); err != nil { if models.IsErrDependenciesLeft(err) { ctx.Error(http.StatusPreconditionFailed, "DependenciesLeft", "cannot close this issue because it still has open dependencies") return diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index a0117794ad54..dac33fe8208a 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -350,6 +350,7 @@ func EditPullRequest(ctx *context.APIContext, form api.EditPullRequestOption) { pr.LoadIssue() issue := pr.Issue + issue.Repo = ctx.Repo.Repository if !issue.IsPoster(ctx.User.ID) && !ctx.Repo.CanWrite(models.UnitTypePullRequests) { ctx.Status(403) @@ -383,7 +384,6 @@ func EditPullRequest(ctx *context.APIContext, form api.EditPullRequestOption) { // Send an empty array ([]) to clear all assignees from the Issue. if ctx.Repo.CanWrite(models.UnitTypePullRequests) && (form.Assignees != nil || len(form.Assignee) > 0) { - err = models.UpdateAPIAssignee(issue, form.Assignee, form.Assignees, ctx.User) if err != nil { if models.IsErrUserNotExist(err) { @@ -422,7 +422,7 @@ func EditPullRequest(ctx *context.APIContext, form api.EditPullRequestOption) { return } if form.State != nil { - if err = issue.ChangeStatus(ctx.User, ctx.Repo.Repository, api.StateClosed == api.StateType(*form.State)); err != nil { + if err = issue.ChangeStatus(ctx.User, api.StateClosed == api.StateType(*form.State)); err != nil { if models.IsErrDependenciesLeft(err) { ctx.Error(http.StatusPreconditionFailed, "DependenciesLeft", "cannot close this pull request because it still has open dependencies") return diff --git a/routers/repo/issue.go b/routers/repo/issue.go index 78be178f4105..f10a3954d007 100644 --- a/routers/repo/issue.go +++ b/routers/repo/issue.go @@ -874,6 +874,7 @@ func GetActionIssue(ctx *context.Context) *models.Issue { ctx.NotFoundOrServerError("GetIssueByIndex", models.IsErrIssueNotExist, err) return nil } + issue.Repo = ctx.Repo.Repository checkIssueRights(ctx, issue) if ctx.Written() { return nil @@ -1055,7 +1056,7 @@ func UpdateIssueStatus(ctx *context.Context) { } for _, issue := range issues { if issue.IsClosed != isClosed { - if err := issue.ChangeStatus(ctx.User, issue.Repo, isClosed); err != nil { + if err := issue.ChangeStatus(ctx.User, isClosed); err != nil { if models.IsErrDependenciesLeft(err) { ctx.JSON(http.StatusPreconditionFailed, map[string]interface{}{ "error": "cannot close this issue because it still has open dependencies", @@ -1132,7 +1133,7 @@ func NewComment(ctx *context.Context, form auth.CreateCommentForm) { ctx.Flash.Info(ctx.Tr("repo.pulls.open_unmerged_pull_exists", pr.Index)) } else { isClosed := form.Status == "close" - if err := issue.ChangeStatus(ctx.User, ctx.Repo.Repository, isClosed); err != nil { + if err := issue.ChangeStatus(ctx.User, isClosed); err != nil { log.Error(4, "ChangeStatus: %v", err) if models.IsErrDependenciesLeft(err) { From c482e12aa9d0bdbaa93d529b8e760833f3983cb4 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 26 Sep 2018 22:42:36 +0800 Subject: [PATCH 3/8] fix bugs --- models/action.go | 3 +++ models/issue.go | 12 +++++++++++- models/issue_assignees_test.go | 2 +- models/issue_dependency_test.go | 5 ++++- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/models/action.go b/models/action.go index 5ba74e7e7baf..b186e90efce7 100644 --- a/models/action.go +++ b/models/action.go @@ -347,6 +347,9 @@ func NewPushCommits() *PushCommits { func (pc *PushCommits) ToAPIPayloadCommits(repoLink string) []*api.PayloadCommit { commits := make([]*api.PayloadCommit, len(pc.Commits)) + if pc.emailUsers == nil { + pc.emailUsers = make(map[string]*User) + } var err error for i, commit := range pc.Commits { authorUsername := "" diff --git a/models/issue.go b/models/issue.go index b80ca47f8d66..25a603201f83 100644 --- a/models/issue.go +++ b/models/issue.go @@ -322,11 +322,13 @@ func (issue *Issue) State() api.StateType { // Required - Poster, Labels, // Optional - Milestone, Assignee, PullRequest func (issue *Issue) APIFormat() *api.Issue { + issue.loadLabels(x) apiLabels := make([]*api.Label, len(issue.Labels)) for i := range issue.Labels { apiLabels[i] = issue.Labels[i].APIFormat() } + issue.loadPoster(x) apiIssue := &api.Issue{ ID: issue.ID, URL: issue.APIURL(), @@ -344,6 +346,8 @@ func (issue *Issue) APIFormat() *api.Issue { if issue.Milestone != nil { apiIssue.Milestone = issue.Milestone.APIFormat() } + issue.loadAssignees(x) + if len(issue.Assignees) > 0 { for _, assignee := range issue.Assignees { apiIssue.Assignees = append(apiIssue.Assignees, assignee.APIFormat()) @@ -351,6 +355,7 @@ func (issue *Issue) APIFormat() *api.Issue { apiIssue.Assignee = issue.Assignees[0].APIFormat() // For compatibility, we're keeping the first assignee as `apiIssue.Assignee` } if issue.IsPull { + issue.loadPullRequest(x) apiIssue.PullRequest = &api.PullRequestMeta{ HasMerged: issue.PullRequest.HasMerged, } @@ -733,6 +738,9 @@ func (issue *Issue) ChangeStatus(doer *User, isClosed bool) (err error) { if err = issue.loadRepo(sess); err != nil { return err } + if err = issue.loadPoster(sess); err != nil { + return err + } if err = issue.changeStatus(sess, doer, isClosed); err != nil { return err @@ -745,8 +753,10 @@ func (issue *Issue) ChangeStatus(doer *User, isClosed bool) (err error) { mode, _ := AccessLevel(issue.Poster, issue.Repo) if issue.IsPull { + if err = issue.loadPullRequest(sess); err != nil { + return err + } // Merge pull request calls issue.changeStatus so we need to handle separately. - issue.PullRequest.Issue = issue apiPullRequest := &api.PullRequestPayload{ Index: issue.Index, PullRequest: issue.PullRequest.APIFormat(), diff --git a/models/issue_assignees_test.go b/models/issue_assignees_test.go index 324781219809..029c211a4b2c 100644 --- a/models/issue_assignees_test.go +++ b/models/issue_assignees_test.go @@ -14,7 +14,7 @@ func TestUpdateAssignee(t *testing.T) { assert.NoError(t, PrepareTestDatabase()) // Fake issue with assignees - issue, err := GetIssueByID(1) + issue, err := GetIssueWithAttrsByID(1) assert.NoError(t, err) // Assign multiple users diff --git a/models/issue_dependency_test.go b/models/issue_dependency_test.go index 571bce3184c4..deb5dbcad7df 100644 --- a/models/issue_dependency_test.go +++ b/models/issue_dependency_test.go @@ -19,8 +19,11 @@ func TestCreateIssueDependency(t *testing.T) { issue1, err := GetIssueByID(1) assert.NoError(t, err) + issue1.LoadAttributes() + issue2, err := GetIssueByID(2) assert.NoError(t, err) + issue2.LoadAttributes() // Create a dependency and check if it was successful err = CreateIssueDependency(user1, issue1, issue2) @@ -44,7 +47,7 @@ func TestCreateIssueDependency(t *testing.T) { assert.False(t, left) // Close #2 and check again - err = issue2.ChangeStatus(user1, issue2.Repo, true) + err = issue2.ChangeStatus(user1, true) assert.NoError(t, err) left, err = IssueNoDependenciesLeft(issue1) From 25bb1c2a4ec405dcad9ee1a1371f172f49b89bf9 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 26 Oct 2018 13:35:45 +0800 Subject: [PATCH 4/8] fix panic --- models/issue.go | 13 ++++++++----- models/issue_assignees.go | 6 +++--- models/pull.go | 17 ++++++++++++----- 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/models/issue.go b/models/issue.go index 25a603201f83..b83002bfd8f9 100644 --- a/models/issue.go +++ b/models/issue.go @@ -290,7 +290,6 @@ func (issue *Issue) HTMLURL() string { } else { path = "issues" } - issue.loadRepo(x) return fmt.Sprintf("%s/%s/%d", issue.Repo.HTMLURL(), path, issue.Index) } @@ -322,13 +321,17 @@ func (issue *Issue) State() api.StateType { // Required - Poster, Labels, // Optional - Milestone, Assignee, PullRequest func (issue *Issue) APIFormat() *api.Issue { - issue.loadLabels(x) + return issue.apiFormat(x) +} + +func (issue *Issue) apiFormat(e Engine) *api.Issue { + issue.loadLabels(e) apiLabels := make([]*api.Label, len(issue.Labels)) for i := range issue.Labels { apiLabels[i] = issue.Labels[i].APIFormat() } - issue.loadPoster(x) + issue.loadPoster(e) apiIssue := &api.Issue{ ID: issue.ID, URL: issue.APIURL(), @@ -346,7 +349,7 @@ func (issue *Issue) APIFormat() *api.Issue { if issue.Milestone != nil { apiIssue.Milestone = issue.Milestone.APIFormat() } - issue.loadAssignees(x) + issue.loadAssignees(e) if len(issue.Assignees) > 0 { for _, assignee := range issue.Assignees { @@ -355,7 +358,7 @@ func (issue *Issue) APIFormat() *api.Issue { apiIssue.Assignee = issue.Assignees[0].APIFormat() // For compatibility, we're keeping the first assignee as `apiIssue.Assignee` } if issue.IsPull { - issue.loadPullRequest(x) + issue.loadPullRequest(e) apiIssue.PullRequest = &api.PullRequestMeta{ HasMerged: issue.PullRequest.HasMerged, } diff --git a/models/issue_assignees.go b/models/issue_assignees.go index f330ade1c8ee..753396e39b49 100644 --- a/models/issue_assignees.go +++ b/models/issue_assignees.go @@ -174,7 +174,7 @@ func (issue *Issue) changeAssignee(sess *xorm.Session, doer *User, assigneeID in apiPullRequest := &api.PullRequestPayload{ Index: issue.Index, PullRequest: issue.PullRequest.APIFormat(), - Repository: issue.Repo.APIFormat(mode), + Repository: issue.Repo.innerAPIFormat(sess, mode, false), Sender: doer.APIFormat(), } if removed { @@ -191,8 +191,8 @@ func (issue *Issue) changeAssignee(sess *xorm.Session, doer *User, assigneeID in apiIssue := &api.IssuePayload{ Index: issue.Index, - Issue: issue.APIFormat(), - Repository: issue.Repo.APIFormat(mode), + Issue: issue.apiFormat(sess), + Repository: issue.Repo.innerAPIFormat(sess, mode, false), Sender: doer.APIFormat(), } if removed { diff --git a/models/pull.go b/models/pull.go index 15a90f92cbcc..0366496caf45 100644 --- a/models/pull.go +++ b/models/pull.go @@ -141,6 +141,10 @@ func (pr *PullRequest) GetGitRefName() string { // Required - Issue // Optional - Merger func (pr *PullRequest) APIFormat() *api.PullRequest { + return pr.apiFormat(x) +} + +func (pr *PullRequest) apiFormat(e Engine) *api.PullRequest { var ( baseBranch *Branch headBranch *Branch @@ -148,16 +152,16 @@ func (pr *PullRequest) APIFormat() *api.PullRequest { headCommit *git.Commit err error ) - apiIssue := pr.Issue.APIFormat() + apiIssue := pr.Issue.apiFormat(e) if pr.BaseRepo == nil { - pr.BaseRepo, err = GetRepositoryByID(pr.BaseRepoID) + pr.BaseRepo, err = getRepositoryByID(e, pr.BaseRepoID) if err != nil { log.Error(log.ERROR, "GetRepositoryById[%d]: %v", pr.ID, err) return nil } } if pr.HeadRepo == nil { - pr.HeadRepo, err = GetRepositoryByID(pr.HeadRepoID) + pr.HeadRepo, err = getRepositoryByID(e, pr.HeadRepoID) if err != nil { log.Error(log.ERROR, "GetRepositoryById[%d]: %v", pr.ID, err) return nil @@ -180,15 +184,18 @@ func (pr *PullRequest) APIFormat() *api.PullRequest { Ref: pr.BaseBranch, Sha: baseCommit.ID.String(), RepoID: pr.BaseRepoID, - Repository: pr.BaseRepo.APIFormat(AccessModeNone), + Repository: pr.BaseRepo.innerAPIFormat(e, AccessModeNone, false), } apiHeadBranchInfo := &api.PRBranchInfo{ Name: pr.HeadBranch, Ref: pr.HeadBranch, Sha: headCommit.ID.String(), RepoID: pr.HeadRepoID, - Repository: pr.HeadRepo.APIFormat(AccessModeNone), + Repository: pr.HeadRepo.innerAPIFormat(e, AccessModeNone, false), } + + pr.Issue.loadRepo(e) + apiPullRequest := &api.PullRequest{ ID: pr.ID, Index: pr.Index, From 1a7e2bd08cf3dd9d09f2739f7e85d18149bf1862 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 30 Nov 2018 11:09:47 +0800 Subject: [PATCH 5/8] fix bugs --- models/issue_stopwatch.go | 8 ++++++++ models/issue_tracked_time.go | 3 +++ 2 files changed, 11 insertions(+) diff --git a/models/issue_stopwatch.go b/models/issue_stopwatch.go index 178b76c5dd4b..d754e7e19eb5 100644 --- a/models/issue_stopwatch.go +++ b/models/issue_stopwatch.go @@ -49,6 +49,10 @@ func CreateOrStopIssueStopwatch(user *User, issue *Issue) error { if err != nil { return err } + if err := issue.loadRepo(x); err != nil { + return err + } + if exists { // Create tracked time out of the time difference between start date and actual date timediff := time.Now().Unix() - int64(sw.CreatedUnix) @@ -112,6 +116,10 @@ func CancelStopwatch(user *User, issue *Issue) error { return err } + if err := issue.loadRepo(x); err != nil { + return err + } + if _, err := CreateComment(&CreateCommentOptions{ Doer: user, Issue: issue, diff --git a/models/issue_tracked_time.go b/models/issue_tracked_time.go index 6592f06d732c..bb3e76712748 100644 --- a/models/issue_tracked_time.go +++ b/models/issue_tracked_time.go @@ -90,6 +90,9 @@ func AddTime(user *User, issue *Issue, time int64) (*TrackedTime, error) { if _, err := x.Insert(tt); err != nil { return nil, err } + if err := issue.loadRepo(x); err != nil { + return nil, err + } if _, err := CreateComment(&CreateCommentOptions{ Issue: issue, Repo: issue.Repo, From 597610c7f6e27d749191c5b6dfc463dff4fdf476 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 30 Nov 2018 11:26:40 +0800 Subject: [PATCH 6/8] fix bugs --- models/action.go | 10 +++++++++- models/issue.go | 5 +++++ models/issue_comment.go | 22 ++++++++++++++++++++++ models/issue_mail.go | 2 +- models/mail.go | 1 + routers/repo/issue.go | 4 ++++ 6 files changed, 42 insertions(+), 2 deletions(-) diff --git a/models/action.go b/models/action.go index b186e90efce7..cde0fb080eba 100644 --- a/models/action.go +++ b/models/action.go @@ -194,6 +194,10 @@ func (a *Action) GetRepoLink() string { // GetCommentLink returns link to action comment. func (a *Action) GetCommentLink() string { + return a.getCommentLink(x) +} + +func (a *Action) getCommentLink(e Engine) string { if a == nil { return "#" } @@ -213,11 +217,15 @@ func (a *Action) GetCommentLink() string { return "#" } - issue, err := GetIssueByID(issueID) + issue, err := getIssueByID(e, issueID) if err != nil { return "#" } + if err = issue.loadRepo(e); err != nil { + return "#" + } + return issue.HTMLURL() } diff --git a/models/issue.go b/models/issue.go index b83002bfd8f9..b1f67ec72b5e 100644 --- a/models/issue.go +++ b/models/issue.go @@ -86,6 +86,11 @@ func (issue *Issue) IsOverdue() bool { return util.TimeStampNow() >= issue.DeadlineUnix } +// LoadRepo loads issue's repository +func (issue *Issue) LoadRepo() error { + return issue.loadRepo(x) +} + func (issue *Issue) loadRepo(e Engine) (err error) { if issue.Repo == nil { issue.Repo, err = getRepositoryByID(e, issue.RepoID) diff --git a/models/issue_comment.go b/models/issue_comment.go index 74215ff20bdc..c630eb18534c 100644 --- a/models/issue_comment.go +++ b/models/issue_comment.go @@ -170,6 +170,11 @@ func (c *Comment) HTMLURL() string { log.Error(4, "LoadIssue(%d): %v", c.IssueID, err) return "" } + err = c.Issue.loadRepo(x) + if err != nil { // Silently dropping errors :unamused: + log.Error(4, "loadRepo(%d): %v", c.Issue.RepoID, err) + return "" + } if c.Type == CommentTypeCode { if c.ReviewID == 0 { return fmt.Sprintf("%s/files#%s", c.Issue.HTMLURL(), c.HashTag()) @@ -198,6 +203,12 @@ func (c *Comment) IssueURL() string { if c.Issue.IsPull { return "" } + + err = c.Issue.loadRepo(x) + if err != nil { // Silently dropping errors :unamused: + log.Error(4, "loadRepo(%d): %v", c.Issue.RepoID, err) + return "" + } return c.Issue.HTMLURL() } @@ -209,6 +220,12 @@ func (c *Comment) PRURL() string { return "" } + err = c.Issue.loadRepo(x) + if err != nil { // Silently dropping errors :unamused: + log.Error(4, "loadRepo(%d): %v", c.Issue.RepoID, err) + return "" + } + if !c.Issue.IsPull { return "" } @@ -470,6 +487,11 @@ func (c *Comment) CodeCommentURL() string { log.Error(4, "LoadIssue(%d): %v", c.IssueID, err) return "" } + err = c.Issue.loadRepo(x) + if err != nil { // Silently dropping errors :unamused: + log.Error(4, "loadRepo(%d): %v", c.Issue.RepoID, err) + return "" + } return fmt.Sprintf("%s/files#%s", c.Issue.HTMLURL(), c.HashTag()) } diff --git a/models/issue_mail.go b/models/issue_mail.go index b78da6d79a5b..7f780e667e77 100644 --- a/models/issue_mail.go +++ b/models/issue_mail.go @@ -39,7 +39,7 @@ func mailIssueCommentToParticipants(e Engine, issue *Issue, doer *User, content // In case the issue poster is not watching the repository and is active, // even if we have duplicated in watchers, can be safely filtered out. - poster, err := GetUserByID(issue.PosterID) + poster, err := getUserByID(e, issue.PosterID) if err != nil { return fmt.Errorf("GetUserByID [%d]: %v", issue.PosterID, err) } diff --git a/models/mail.go b/models/mail.go index 6b69e7b2ad35..7e6d4e026512 100644 --- a/models/mail.go +++ b/models/mail.go @@ -151,6 +151,7 @@ func composeTplData(subject, body, link string) map[string]interface{} { func composeIssueCommentMessage(issue *Issue, doer *User, content string, comment *Comment, tplName base.TplName, tos []string, info string) *mailer.Message { subject := issue.mailSubject() + issue.LoadRepo() body := string(markup.RenderByType(markdown.MarkupName, []byte(content), issue.Repo.HTMLURL(), issue.Repo.ComposeMetas())) data := make(map[string]interface{}, 10) diff --git a/routers/repo/issue.go b/routers/repo/issue.go index f10a3954d007..d1df06609864 100644 --- a/routers/repo/issue.go +++ b/routers/repo/issue.go @@ -683,6 +683,10 @@ func ViewIssue(ctx *context.Context) { ctx.ServerError("GetIssueByID", err) return } + if err = otherIssue.LoadRepo(); err != nil { + ctx.ServerError("LoadRepo", err) + return + } // Add link to the issue of the already running stopwatch ctx.Data["OtherStopwatchURL"] = otherIssue.HTMLURL() } From 745d6a72e4f641efdcbf5c5eb676db03a8e982cf Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 30 Nov 2018 19:56:23 +0800 Subject: [PATCH 7/8] fix bugs --- models/issue.go | 8 ++++++++ models/issue_comment.go | 6 ++++++ models/issue_comment_list.go | 5 +++++ models/pull.go | 4 ++++ routers/api/v1/repo/issue_comment.go | 9 +++++++++ 5 files changed, 32 insertions(+) diff --git a/models/issue.go b/models/issue.go index b1f67ec72b5e..2fb2ff30dd3a 100644 --- a/models/issue.go +++ b/models/issue.go @@ -337,6 +337,7 @@ func (issue *Issue) apiFormat(e Engine) *api.Issue { } issue.loadPoster(e) + issue.loadRepo(e) apiIssue := &api.Issue{ ID: issue.ID, URL: issue.APIURL(), @@ -815,6 +816,10 @@ func (issue *Issue) ChangeTitle(doer *User, title string) (err error) { return fmt.Errorf("updateIssueCols: %v", err) } + if err = issue.loadRepo(sess); err != nil { + return fmt.Errorf("loadRepo: %v", err) + } + if _, err = createChangeTitleComment(sess, doer, issue.Repo, issue, oldTitle, title); err != nil { return fmt.Errorf("createChangeTitleComment: %v", err) } @@ -825,6 +830,9 @@ func (issue *Issue) ChangeTitle(doer *User, title string) (err error) { mode, _ := AccessLevel(issue.Poster, issue.Repo) if issue.IsPull { + if err = issue.loadPullRequest(sess); err != nil { + return fmt.Errorf("loadPullRequest: %v", err) + } issue.PullRequest.Issue = issue err = PrepareWebhooks(issue.Repo, HookEventPullRequest, &api.PullRequestPayload{ Action: api.HookIssueEdited, diff --git a/models/issue_comment.go b/models/issue_comment.go index c630eb18534c..577de1d84d62 100644 --- a/models/issue_comment.go +++ b/models/issue_comment.go @@ -1021,6 +1021,9 @@ func UpdateComment(doer *User, c *Comment, oldContent string) error { UpdateIssueIndexer(c.IssueID) } + if err := c.LoadPoster(); err != nil { + return err + } if err := c.LoadIssue(); err != nil { return err } @@ -1078,6 +1081,9 @@ func DeleteComment(doer *User, comment *Comment) error { UpdateIssueIndexer(comment.IssueID) } + if err := comment.LoadPoster(); err != nil { + return err + } if err := comment.LoadIssue(); err != nil { return err } diff --git a/models/issue_comment_list.go b/models/issue_comment_list.go index 32ed0bd63eb7..35c1253f6b35 100644 --- a/models/issue_comment_list.go +++ b/models/issue_comment_list.go @@ -17,6 +17,11 @@ func (comments CommentList) getPosterIDs() []int64 { return keysInt64(commentIDs) } +// LoadPosters loads posters from database +func (comments CommentList) LoadPosters() error { + return comments.loadPosters(x) +} + func (comments CommentList) loadPosters(e Engine) error { if len(comments) == 0 { return nil diff --git a/models/pull.go b/models/pull.go index 0366496caf45..a828500c5f07 100644 --- a/models/pull.go +++ b/models/pull.go @@ -152,6 +152,10 @@ func (pr *PullRequest) apiFormat(e Engine) *api.PullRequest { headCommit *git.Commit err error ) + if err = pr.Issue.loadRepo(e); err != nil { + log.Error(log.ERROR, "loadRepo[%d]: %v", pr.ID, err) + return nil + } apiIssue := pr.Issue.apiFormat(e) if pr.BaseRepo == nil { pr.BaseRepo, err = getRepositoryByID(e, pr.BaseRepoID) diff --git a/routers/api/v1/repo/issue_comment.go b/routers/api/v1/repo/issue_comment.go index 85b599e9f7f2..a3fc6f41f344 100644 --- a/routers/api/v1/repo/issue_comment.go +++ b/routers/api/v1/repo/issue_comment.go @@ -68,6 +68,10 @@ func ListIssueComments(ctx *context.APIContext) { } apiComments := make([]*api.Comment, len(comments)) + if err = models.CommentList(comments).LoadPosters(); err != nil { + ctx.Error(500, "LoadPosters", err) + return + } for i := range comments { apiComments[i] = comments[i].APIFormat() } @@ -114,6 +118,11 @@ func ListRepoIssueComments(ctx *context.APIContext) { return } + if err = models.CommentList(comments).LoadPosters(); err != nil { + ctx.Error(500, "LoadPosters", err) + return + } + apiComments := make([]*api.Comment, len(comments)) for i := range comments { apiComments[i] = comments[i].APIFormat() From 0086f739378dbab77b861a23716d8cbf6a930ff6 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 30 Nov 2018 20:15:37 +0800 Subject: [PATCH 8/8] fix bugs --- routers/repo/pull.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/routers/repo/pull.go b/routers/repo/pull.go index f4c7fcfe7d64..0d5cec802c57 100644 --- a/routers/repo/pull.go +++ b/routers/repo/pull.go @@ -1069,8 +1069,12 @@ func DownloadPullDiff(ctx *context.Context) { return } - pr := issue.PullRequest + if err = issue.LoadPullRequest(); err != nil { + ctx.ServerError("LoadPullRequest", err) + return + } + pr := issue.PullRequest if err = pr.GetBaseRepo(); err != nil { ctx.ServerError("GetBaseRepo", err) return @@ -1103,8 +1107,12 @@ func DownloadPullPatch(ctx *context.Context) { return } - pr := issue.PullRequest + if err = issue.LoadPullRequest(); err != nil { + ctx.ServerError("LoadPullRequest", err) + return + } + pr := issue.PullRequest if err = pr.GetHeadRepo(); err != nil { ctx.ServerError("GetHeadRepo", err) return