diff --git a/models/pull.go b/models/pull.go index 48d234054437..02b5e98c4931 100644 --- a/models/pull.go +++ b/models/pull.go @@ -10,7 +10,6 @@ import ( "io" "strings" - "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" @@ -215,143 +214,6 @@ func (pr *PullRequest) GetDefaultMergeMessage() string { return fmt.Sprintf("Merge pull request '%s' (#%d) from %s:%s into %s", pr.Issue.Title, pr.Issue.Index, pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseBranch) } -// GetCommitMessages returns the commit messages between head and merge base (if there is one) -func (pr *PullRequest) GetCommitMessages() string { - if err := pr.LoadIssue(); err != nil { - log.Error("Cannot load issue %d for PR id %d: Error: %v", pr.IssueID, pr.ID, err) - return "" - } - - if err := pr.Issue.LoadPoster(); err != nil { - log.Error("Cannot load poster %d for pr id %d, index %d Error: %v", pr.Issue.PosterID, pr.ID, pr.Index, err) - return "" - } - - if pr.HeadRepo == nil { - var err error - pr.HeadRepo, err = GetRepositoryByID(pr.HeadRepoID) - if err != nil { - log.Error("GetRepositoryById[%d]: %v", pr.HeadRepoID, err) - return "" - } - } - - gitRepo, err := git.OpenRepository(pr.HeadRepo.RepoPath()) - if err != nil { - log.Error("Unable to open head repository: Error: %v", err) - return "" - } - defer gitRepo.Close() - - headCommit, err := gitRepo.GetBranchCommit(pr.HeadBranch) - if err != nil { - log.Error("Unable to get head commit: %s Error: %v", pr.HeadBranch, err) - return "" - } - - mergeBase, err := gitRepo.GetCommit(pr.MergeBase) - if err != nil { - log.Error("Unable to get merge base commit: %s Error: %v", pr.MergeBase, err) - return "" - } - - limit := setting.Repository.PullRequest.DefaultMergeMessageCommitsLimit - - list, err := gitRepo.CommitsBetweenLimit(headCommit, mergeBase, limit, 0) - if err != nil { - log.Error("Unable to get commits between: %s %s Error: %v", pr.HeadBranch, pr.MergeBase, err) - return "" - } - - maxSize := setting.Repository.PullRequest.DefaultMergeMessageSize - - posterSig := pr.Issue.Poster.NewGitSig().String() - - authorsMap := map[string]bool{} - authors := make([]string, 0, list.Len()) - stringBuilder := strings.Builder{} - element := list.Front() - for element != nil { - commit := element.Value.(*git.Commit) - - if maxSize < 0 || stringBuilder.Len() < maxSize { - toWrite := []byte(commit.CommitMessage) - if len(toWrite) > maxSize-stringBuilder.Len() && maxSize > -1 { - toWrite = append(toWrite[:maxSize-stringBuilder.Len()], "..."...) - } - if _, err := stringBuilder.Write(toWrite); err != nil { - log.Error("Unable to write commit message Error: %v", err) - return "" - } - - if _, err := stringBuilder.WriteRune('\n'); err != nil { - log.Error("Unable to write commit message Error: %v", err) - return "" - } - } - - authorString := commit.Author.String() - if !authorsMap[authorString] && authorString != posterSig { - authors = append(authors, authorString) - authorsMap[authorString] = true - } - element = element.Next() - } - - // Consider collecting the remaining authors - if limit >= 0 && setting.Repository.PullRequest.DefaultMergeMessageAllAuthors { - skip := limit - limit = 30 - for { - list, err := gitRepo.CommitsBetweenLimit(headCommit, mergeBase, limit, skip) - if err != nil { - log.Error("Unable to get commits between: %s %s Error: %v", pr.HeadBranch, pr.MergeBase, err) - return "" - - } - if list.Len() == 0 { - break - } - element := list.Front() - for element != nil { - commit := element.Value.(*git.Commit) - - authorString := commit.Author.String() - if !authorsMap[authorString] && authorString != posterSig { - authors = append(authors, authorString) - authorsMap[authorString] = true - } - element = element.Next() - } - - } - } - - if len(authors) > 0 { - if _, err := stringBuilder.WriteRune('\n'); err != nil { - log.Error("Unable to write to string builder Error: %v", err) - return "" - } - } - - for _, author := range authors { - if _, err := stringBuilder.Write([]byte("Co-authored-by: ")); err != nil { - log.Error("Unable to write to string builder Error: %v", err) - return "" - } - if _, err := stringBuilder.Write([]byte(author)); err != nil { - log.Error("Unable to write to string builder Error: %v", err) - return "" - } - if _, err := stringBuilder.WriteRune('\n'); err != nil { - log.Error("Unable to write to string builder Error: %v", err) - return "" - } - } - - return stringBuilder.String() -} - // ReviewCount represents a count of Reviews type ReviewCount struct { IssueID int64 @@ -465,39 +327,6 @@ func (pr *PullRequest) CanAutoMerge() bool { return pr.Status == PullRequestStatusMergeable } -// GetLastCommitStatus returns the last commit status for this pull request. -func (pr *PullRequest) GetLastCommitStatus() (status *CommitStatus, err error) { - if err = pr.LoadHeadRepo(); err != nil { - return nil, err - } - - if pr.HeadRepo == nil { - return nil, ErrPullRequestHeadRepoMissing{pr.ID, pr.HeadRepoID} - } - - headGitRepo, err := git.OpenRepository(pr.HeadRepo.RepoPath()) - if err != nil { - return nil, err - } - defer headGitRepo.Close() - - lastCommitID, err := headGitRepo.GetBranchCommitID(pr.HeadBranch) - if err != nil { - return nil, err - } - - err = pr.LoadBaseRepo() - if err != nil { - return nil, err - } - - statusList, err := GetLatestCommitStatus(pr.BaseRepo, lastCommitID, 0) - if err != nil { - return nil, err - } - return CalcCommitStatus(statusList), nil -} - // MergeStyle represents the approach to merge commits into base branch. type MergeStyle string @@ -786,35 +615,6 @@ func (pr *PullRequest) GetWorkInProgressPrefix() string { return "" } -// IsHeadEqualWithBranch returns if the commits of branchName are available in pull request head -func (pr *PullRequest) IsHeadEqualWithBranch(branchName string) (bool, error) { - var err error - if err = pr.LoadBaseRepo(); err != nil { - return false, err - } - baseGitRepo, err := git.OpenRepository(pr.BaseRepo.RepoPath()) - if err != nil { - return false, err - } - baseCommit, err := baseGitRepo.GetBranchCommit(branchName) - if err != nil { - return false, err - } - - if err = pr.LoadHeadRepo(); err != nil { - return false, err - } - headGitRepo, err := git.OpenRepository(pr.HeadRepo.RepoPath()) - if err != nil { - return false, err - } - headCommit, err := headGitRepo.GetBranchCommit(pr.HeadBranch) - if err != nil { - return false, err - } - return baseCommit.HasPreviousCommit(headCommit.ID) -} - // IsSameRepo returns true if base repo and head repo is the same func (pr *PullRequest) IsSameRepo() bool { return pr.BaseRepoID == pr.HeadRepoID diff --git a/routers/repo/issue.go b/routers/repo/issue.go index 0a059bf78962..3655de7ed44f 100644 --- a/routers/repo/issue.go +++ b/routers/repo/issue.go @@ -240,7 +240,7 @@ func issues(ctx *context.Context, milestoneID int64, isPullOption util.OptionalB return } - commitStatus[issues[i].PullRequest.ID], _ = issues[i].PullRequest.GetLastCommitStatus() + commitStatus[issues[i].PullRequest.ID], _ = pull_service.GetLastCommitStatus(issues[i].PullRequest) } } diff --git a/routers/repo/pull.go b/routers/repo/pull.go index 55b8bbb42f52..ef000208f441 100644 --- a/routers/repo/pull.go +++ b/routers/repo/pull.go @@ -433,6 +433,7 @@ func PrepareViewPullInfo(ctx *context.Context, issue *models.Issue) *git.Compare return nil } ctx.Data["Divergence"] = divergence + ctx.Data["GetCommitMessages"] = pull_service.GetCommitMessages(pull) } sha, err := baseGitRepo.GetRefCommitID(pull.GetGitRefName()) diff --git a/routers/user/home.go b/routers/user/home.go index 3807025ea52f..816968562fd6 100644 --- a/routers/user/home.go +++ b/routers/user/home.go @@ -22,6 +22,7 @@ import ( "code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" + pull_service "code.gitea.io/gitea/services/pull" "github.com/keybase/go-crypto/openpgp" "github.com/keybase/go-crypto/openpgp/armor" @@ -553,7 +554,7 @@ func Issues(ctx *context.Context) { issue.Repo = showReposMap[issue.RepoID] if isPullList { - commitStatus[issue.PullRequest.ID], _ = issue.PullRequest.GetLastCommitStatus() + commitStatus[issue.PullRequest.ID], _ = pull_service.GetLastCommitStatus(issue.PullRequest) } } diff --git a/services/pull/pull.go b/services/pull/pull.go index ce5c4ff22ffc..2797294980eb 100644 --- a/services/pull/pull.go +++ b/services/pull/pull.go @@ -19,6 +19,7 @@ import ( "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/notification" + "code.gitea.io/gitea/modules/setting" issue_service "code.gitea.io/gitea/services/issue" "github.com/unknwon/com" @@ -79,7 +80,7 @@ func ChangeTargetBranch(pr *models.PullRequest, doer *models.User, targetBranch } // Check if branches are equal - branchesEqual, err := pr.IsHeadEqualWithBranch(targetBranch) + branchesEqual, err := IsHeadEqualWithBranch(pr, targetBranch) if err != nil { return err } @@ -454,3 +455,202 @@ func CloseRepoBranchesPulls(doer *models.User, repo *models.Repository) error { } return nil } + +// GetCommitMessages returns the commit messages between head and merge base (if there is one) +func GetCommitMessages(pr *models.PullRequest) string { + if err := pr.LoadIssue(); err != nil { + log.Error("Cannot load issue %d for PR id %d: Error: %v", pr.IssueID, pr.ID, err) + return "" + } + + if err := pr.Issue.LoadPoster(); err != nil { + log.Error("Cannot load poster %d for pr id %d, index %d Error: %v", pr.Issue.PosterID, pr.ID, pr.Index, err) + return "" + } + + if pr.HeadRepo == nil { + var err error + pr.HeadRepo, err = models.GetRepositoryByID(pr.HeadRepoID) + if err != nil { + log.Error("GetRepositoryById[%d]: %v", pr.HeadRepoID, err) + return "" + } + } + + gitRepo, err := git.OpenRepository(pr.HeadRepo.RepoPath()) + if err != nil { + log.Error("Unable to open head repository: Error: %v", err) + return "" + } + defer gitRepo.Close() + + headCommit, err := gitRepo.GetBranchCommit(pr.HeadBranch) + if err != nil { + log.Error("Unable to get head commit: %s Error: %v", pr.HeadBranch, err) + return "" + } + + mergeBase, err := gitRepo.GetCommit(pr.MergeBase) + if err != nil { + log.Error("Unable to get merge base commit: %s Error: %v", pr.MergeBase, err) + return "" + } + + limit := setting.Repository.PullRequest.DefaultMergeMessageCommitsLimit + + list, err := gitRepo.CommitsBetweenLimit(headCommit, mergeBase, limit, 0) + if err != nil { + log.Error("Unable to get commits between: %s %s Error: %v", pr.HeadBranch, pr.MergeBase, err) + return "" + } + + maxSize := setting.Repository.PullRequest.DefaultMergeMessageSize + + posterSig := pr.Issue.Poster.NewGitSig().String() + + authorsMap := map[string]bool{} + authors := make([]string, 0, list.Len()) + stringBuilder := strings.Builder{} + element := list.Front() + for element != nil { + commit := element.Value.(*git.Commit) + + if maxSize < 0 || stringBuilder.Len() < maxSize { + toWrite := []byte(commit.CommitMessage) + if len(toWrite) > maxSize-stringBuilder.Len() && maxSize > -1 { + toWrite = append(toWrite[:maxSize-stringBuilder.Len()], "..."...) + } + if _, err := stringBuilder.Write(toWrite); err != nil { + log.Error("Unable to write commit message Error: %v", err) + return "" + } + + if _, err := stringBuilder.WriteRune('\n'); err != nil { + log.Error("Unable to write commit message Error: %v", err) + return "" + } + } + + authorString := commit.Author.String() + if !authorsMap[authorString] && authorString != posterSig { + authors = append(authors, authorString) + authorsMap[authorString] = true + } + element = element.Next() + } + + // Consider collecting the remaining authors + if limit >= 0 && setting.Repository.PullRequest.DefaultMergeMessageAllAuthors { + skip := limit + limit = 30 + for { + list, err := gitRepo.CommitsBetweenLimit(headCommit, mergeBase, limit, skip) + if err != nil { + log.Error("Unable to get commits between: %s %s Error: %v", pr.HeadBranch, pr.MergeBase, err) + return "" + + } + if list.Len() == 0 { + break + } + element := list.Front() + for element != nil { + commit := element.Value.(*git.Commit) + + authorString := commit.Author.String() + if !authorsMap[authorString] && authorString != posterSig { + authors = append(authors, authorString) + authorsMap[authorString] = true + } + element = element.Next() + } + + } + } + + if len(authors) > 0 { + if _, err := stringBuilder.WriteRune('\n'); err != nil { + log.Error("Unable to write to string builder Error: %v", err) + return "" + } + } + + for _, author := range authors { + if _, err := stringBuilder.Write([]byte("Co-authored-by: ")); err != nil { + log.Error("Unable to write to string builder Error: %v", err) + return "" + } + if _, err := stringBuilder.Write([]byte(author)); err != nil { + log.Error("Unable to write to string builder Error: %v", err) + return "" + } + if _, err := stringBuilder.WriteRune('\n'); err != nil { + log.Error("Unable to write to string builder Error: %v", err) + return "" + } + } + + return stringBuilder.String() +} + +// GetLastCommitStatus returns the last commit status for this pull request. +func GetLastCommitStatus(pr *models.PullRequest) (status *models.CommitStatus, err error) { + if err = pr.LoadHeadRepo(); err != nil { + return nil, err + } + + if pr.HeadRepo == nil { + return nil, models.ErrPullRequestHeadRepoMissing{ID: pr.ID, HeadRepoID: pr.HeadRepoID} + } + + headGitRepo, err := git.OpenRepository(pr.HeadRepo.RepoPath()) + if err != nil { + return nil, err + } + defer headGitRepo.Close() + + lastCommitID, err := headGitRepo.GetBranchCommitID(pr.HeadBranch) + if err != nil { + return nil, err + } + + err = pr.LoadBaseRepo() + if err != nil { + return nil, err + } + + statusList, err := models.GetLatestCommitStatus(pr.BaseRepo, lastCommitID, 0) + if err != nil { + return nil, err + } + return models.CalcCommitStatus(statusList), nil +} + +// IsHeadEqualWithBranch returns if the commits of branchName are available in pull request head +func IsHeadEqualWithBranch(pr *models.PullRequest, branchName string) (bool, error) { + var err error + if err = pr.LoadBaseRepo(); err != nil { + return false, err + } + baseGitRepo, err := git.OpenRepository(pr.BaseRepo.RepoPath()) + if err != nil { + return false, err + } + baseCommit, err := baseGitRepo.GetBranchCommit(branchName) + if err != nil { + return false, err + } + + if err = pr.LoadHeadRepo(); err != nil { + return false, err + } + headGitRepo, err := git.OpenRepository(pr.HeadRepo.RepoPath()) + if err != nil { + return false, err + } + headCommit, err := headGitRepo.GetBranchCommit(pr.HeadBranch) + if err != nil { + return false, err + } + return baseCommit.HasPreviousCommit(headCommit.ID) +} diff --git a/templates/repo/issue/view_content/pull.tmpl b/templates/repo/issue/view_content/pull.tmpl index 09c7cd1d9f46..baad7f864df5 100644 --- a/templates/repo/issue/view_content/pull.tmpl +++ b/templates/repo/issue/view_content/pull.tmpl @@ -234,7 +234,6 @@ {{end}} {{if $prUnit.PullRequestsConfig.AllowSquash}} - {{$commitMessages := .Issue.PullRequest.GetCommitMessages}}