| @@ -10,7 +10,6 @@ import ( | |||||
| "io" | "io" | ||||
| "strings" | "strings" | ||||
| "code.gitea.io/gitea/modules/git" | |||||
| "code.gitea.io/gitea/modules/log" | "code.gitea.io/gitea/modules/log" | ||||
| "code.gitea.io/gitea/modules/setting" | "code.gitea.io/gitea/modules/setting" | ||||
| "code.gitea.io/gitea/modules/timeutil" | "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) | 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 | // ReviewCount represents a count of Reviews | ||||
| type ReviewCount struct { | type ReviewCount struct { | ||||
| IssueID int64 | IssueID int64 | ||||
| @@ -465,39 +327,6 @@ func (pr *PullRequest) CanAutoMerge() bool { | |||||
| return pr.Status == PullRequestStatusMergeable | 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. | // MergeStyle represents the approach to merge commits into base branch. | ||||
| type MergeStyle string | type MergeStyle string | ||||
| @@ -786,35 +615,6 @@ func (pr *PullRequest) GetWorkInProgressPrefix() string { | |||||
| return "" | 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 | // IsSameRepo returns true if base repo and head repo is the same | ||||
| func (pr *PullRequest) IsSameRepo() bool { | func (pr *PullRequest) IsSameRepo() bool { | ||||
| return pr.BaseRepoID == pr.HeadRepoID | return pr.BaseRepoID == pr.HeadRepoID | ||||
| @@ -240,7 +240,7 @@ func issues(ctx *context.Context, milestoneID int64, isPullOption util.OptionalB | |||||
| return | return | ||||
| } | } | ||||
| commitStatus[issues[i].PullRequest.ID], _ = issues[i].PullRequest.GetLastCommitStatus() | |||||
| commitStatus[issues[i].PullRequest.ID], _ = pull_service.GetLastCommitStatus(issues[i].PullRequest) | |||||
| } | } | ||||
| } | } | ||||
| @@ -433,6 +433,7 @@ func PrepareViewPullInfo(ctx *context.Context, issue *models.Issue) *git.Compare | |||||
| return nil | return nil | ||||
| } | } | ||||
| ctx.Data["Divergence"] = divergence | ctx.Data["Divergence"] = divergence | ||||
| ctx.Data["GetCommitMessages"] = pull_service.GetCommitMessages(pull) | |||||
| } | } | ||||
| sha, err := baseGitRepo.GetRefCommitID(pull.GetGitRefName()) | sha, err := baseGitRepo.GetRefCommitID(pull.GetGitRefName()) | ||||
| @@ -22,6 +22,7 @@ import ( | |||||
| "code.gitea.io/gitea/modules/markup/markdown" | "code.gitea.io/gitea/modules/markup/markdown" | ||||
| "code.gitea.io/gitea/modules/setting" | "code.gitea.io/gitea/modules/setting" | ||||
| "code.gitea.io/gitea/modules/util" | "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" | ||||
| "github.com/keybase/go-crypto/openpgp/armor" | "github.com/keybase/go-crypto/openpgp/armor" | ||||
| @@ -553,7 +554,7 @@ func Issues(ctx *context.Context) { | |||||
| issue.Repo = showReposMap[issue.RepoID] | issue.Repo = showReposMap[issue.RepoID] | ||||
| if isPullList { | if isPullList { | ||||
| commitStatus[issue.PullRequest.ID], _ = issue.PullRequest.GetLastCommitStatus() | |||||
| commitStatus[issue.PullRequest.ID], _ = pull_service.GetLastCommitStatus(issue.PullRequest) | |||||
| } | } | ||||
| } | } | ||||
| @@ -19,6 +19,7 @@ import ( | |||||
| "code.gitea.io/gitea/modules/graceful" | "code.gitea.io/gitea/modules/graceful" | ||||
| "code.gitea.io/gitea/modules/log" | "code.gitea.io/gitea/modules/log" | ||||
| "code.gitea.io/gitea/modules/notification" | "code.gitea.io/gitea/modules/notification" | ||||
| "code.gitea.io/gitea/modules/setting" | |||||
| issue_service "code.gitea.io/gitea/services/issue" | issue_service "code.gitea.io/gitea/services/issue" | ||||
| "github.com/unknwon/com" | "github.com/unknwon/com" | ||||
| @@ -79,7 +80,7 @@ func ChangeTargetBranch(pr *models.PullRequest, doer *models.User, targetBranch | |||||
| } | } | ||||
| // Check if branches are equal | // Check if branches are equal | ||||
| branchesEqual, err := pr.IsHeadEqualWithBranch(targetBranch) | |||||
| branchesEqual, err := IsHeadEqualWithBranch(pr, targetBranch) | |||||
| if err != nil { | if err != nil { | ||||
| return err | return err | ||||
| } | } | ||||
| @@ -454,3 +455,202 @@ func CloseRepoBranchesPulls(doer *models.User, repo *models.Repository) error { | |||||
| } | } | ||||
| return nil | 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) | |||||
| } | |||||
| @@ -234,7 +234,6 @@ | |||||
| </div> | </div> | ||||
| {{end}} | {{end}} | ||||
| {{if $prUnit.PullRequestsConfig.AllowSquash}} | {{if $prUnit.PullRequestsConfig.AllowSquash}} | ||||
| {{$commitMessages := .Issue.PullRequest.GetCommitMessages}} | |||||
| <div class="ui form squash-fields" style="display: none"> | <div class="ui form squash-fields" style="display: none"> | ||||
| <form action="{{.Link}}/merge" method="post"> | <form action="{{.Link}}/merge" method="post"> | ||||
| {{.CsrfTokenHtml}} | {{.CsrfTokenHtml}} | ||||
| @@ -242,7 +241,7 @@ | |||||
| <input type="text" name="merge_title_field" value="{{.Issue.PullRequest.GetDefaultSquashMessage}}"> | <input type="text" name="merge_title_field" value="{{.Issue.PullRequest.GetDefaultSquashMessage}}"> | ||||
| </div> | </div> | ||||
| <div class="field"> | <div class="field"> | ||||
| <textarea name="merge_message_field" rows="5" placeholder="{{$.i18n.Tr "repo.editor.commit_message_desc"}}">{{$commitMessages}}Reviewed-on: {{$.Issue.HTMLURL}} {{$approvers}}</textarea> | |||||
| <textarea name="merge_message_field" rows="5" placeholder="{{$.i18n.Tr "repo.editor.commit_message_desc"}}">{{.GetCommitMessages}}Reviewed-on: {{$.Issue.HTMLURL}} {{$approvers}}</textarea> | |||||
| </div> | </div> | ||||
| <button class="ui green button" type="submit" name="do" value="squash"> | <button class="ui green button" type="submit" name="do" value="squash"> | ||||
| {{$.i18n.Tr "repo.pulls.squash_merge_pull_request"}} | {{$.i18n.Tr "repo.pulls.squash_merge_pull_request"}} | ||||