diff options
author | Lunny Xiao <xiaolunwen@gmail.com> | 2022-05-08 20:32:45 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-05-08 20:32:45 +0800 |
commit | 4344a6410788f30848e5153f6356dcdd0774bebc (patch) | |
tree | 6eba1230f1ac672f0eb0a36f148949470dd50048 /services | |
parent | 5ca224a789394d00cc1efb5afcae7b87aa7d1e49 (diff) | |
download | gitea-4344a6410788f30848e5153f6356dcdd0774bebc.tar.gz gitea-4344a6410788f30848e5153f6356dcdd0774bebc.zip |
Allow custom default merge message with .gitea/default_merge_message/<merge_style>_TEMPLATE.md (#18177)
* Allow custom default merge message with .gitea/MERGE_MESSAGE_<merge_style>_TEMPLATE.md
* Some improvements
* Follow some advices
* Fix bug
* Fix bug
* Fix lint
* Fix close comment
* Fix test
* Fix and docs
* Improve codes
* Update docs and remove unnecessary variables
* return error for GetDefaultMergeMessage
* Fix test
* improve code
* ignore unknow unit type
* return error for GetDefaultMergeMessage
* Update services/pull/merge.go
* Some improvements
* Follow some advices
* Fix bug
* Fix lint
* Improve codes
* Update docs and remove unnecessary variables
* return error for GetDefaultMergeMessage
* improve code
* Handle deleted HeadRepo in GetDefaultMergeMessage
Signed-off-by: Andrew Thornton <art27@cantab.net>
* Fix test
* Fix test
Co-authored-by: zeripath <art27@cantab.net>
Diffstat (limited to 'services')
-rw-r--r-- | services/automerge/automerge.go | 2 | ||||
-rw-r--r-- | services/forms/repo_form.go | 26 | ||||
-rw-r--r-- | services/pull/merge.go | 109 | ||||
-rw-r--r-- | services/pull/pull_test.go | 60 |
4 files changed, 162 insertions, 35 deletions
diff --git a/services/automerge/automerge.go b/services/automerge/automerge.go index 389546ed57..e098f2cec0 100644 --- a/services/automerge/automerge.go +++ b/services/automerge/automerge.go @@ -234,7 +234,7 @@ func handlePull(pullID int64, sha string) { defer baseGitRepo.Close() } - if err := pull_service.Merge(pr, doer, baseGitRepo, scheduledPRM.MergeStyle, "", scheduledPRM.Message); err != nil { + if err := pull_service.Merge(ctx, pr, doer, baseGitRepo, scheduledPRM.MergeStyle, "", scheduledPRM.Message); err != nil { log.Error("pull_service.Merge: %v", err) return } diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index bacee9a13c..6c61ed00ec 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -6,7 +6,6 @@ package forms import ( - stdContext "context" "net/http" "net/url" "strings" @@ -602,31 +601,6 @@ func (f *MergePullRequestForm) Validate(req *http.Request, errs binding.Errors) return middleware.Validate(errs, ctx.Data, f, ctx.Locale) } -// SetDefaults if not provided for mergestyle and commit message -func (f *MergePullRequestForm) SetDefaults(ctx stdContext.Context, pr *models.PullRequest) (err error) { - if f.Do == "" { - f.Do = "merge" - } - - f.MergeTitleField = strings.TrimSpace(f.MergeTitleField) - if len(f.MergeTitleField) == 0 { - switch f.Do { - case "merge", "rebase-merge": - f.MergeTitleField, err = pr.GetDefaultMergeMessage(ctx) - case "squash": - f.MergeTitleField, err = pr.GetDefaultSquashMessage(ctx) - } - } - - f.MergeMessageField = strings.TrimSpace(f.MergeMessageField) - if len(f.MergeMessageField) > 0 { - f.MergeTitleField += "\n\n" + f.MergeMessageField - f.MergeMessageField = "" - } - - return -} - // CodeCommentForm form for adding code comments for PRs type CodeCommentForm struct { Origin string `binding:"Required;In(timeline,diff)"` diff --git a/services/pull/merge.go b/services/pull/merge.go index 8cc4d88888..b14abcd780 100644 --- a/services/pull/merge.go +++ b/services/pull/merge.go @@ -13,6 +13,7 @@ import ( "os" "path/filepath" "regexp" + "strconv" "strings" "time" @@ -33,9 +34,101 @@ import ( issue_service "code.gitea.io/gitea/services/issue" ) +// GetDefaultMergeMessage returns default message used when merging pull request +func GetDefaultMergeMessage(baseGitRepo *git.Repository, pr *models.PullRequest, mergeStyle repo_model.MergeStyle) (string, error) { + if err := pr.LoadHeadRepo(); err != nil { + return "", err + } + if err := pr.LoadBaseRepo(); err != nil { + return "", err + } + if pr.BaseRepo == nil { + return "", repo_model.ErrRepoNotExist{ID: pr.BaseRepoID} + } + + if err := pr.LoadIssue(); err != nil { + return "", err + } + + isExternalTracker := pr.BaseRepo.UnitEnabled(unit.TypeExternalTracker) + issueReference := "#" + if isExternalTracker { + issueReference = "!" + } + + if mergeStyle != "" { + templateFilepath := fmt.Sprintf(".gitea/default_merge_message/%s_TEMPLATE.md", strings.ToUpper(string(mergeStyle))) + commit, err := baseGitRepo.GetBranchCommit(pr.BaseRepo.DefaultBranch) + if err != nil { + return "", err + } + templateContent, err := commit.GetFileContent(templateFilepath, setting.Repository.PullRequest.DefaultMergeMessageSize) + if err != nil { + if !git.IsErrNotExist(err) { + return "", err + } + } else { + vars := map[string]string{ + "BaseRepoOwnerName": pr.BaseRepo.OwnerName, + "BaseRepoName": pr.BaseRepo.Name, + "BaseBranch": pr.BaseBranch, + "HeadRepoOwnerName": "", + "HeadRepoName": "", + "HeadBranch": pr.HeadBranch, + "PullRequestTitle": pr.Issue.Title, + "PullRequestDescription": pr.Issue.Content, + "PullRequestPosterName": pr.Issue.Poster.Name, + "PullRequestIndex": strconv.FormatInt(pr.Index, 10), + "PullRequestReference": fmt.Sprintf("%s%d", issueReference, pr.Index), + } + if pr.HeadRepo != nil { + vars["HeadRepoOwnerName"] = pr.HeadRepo.OwnerName + vars["HeadRepoName"] = pr.HeadRepo.Name + } + refs, err := pr.ResolveCrossReferences(baseGitRepo.Ctx) + if err == nil { + closeIssueIndexes := make([]string, 0, len(refs)) + closeWord := "close" + if len(setting.Repository.PullRequest.CloseKeywords) > 0 { + closeWord = setting.Repository.PullRequest.CloseKeywords[0] + } + for _, ref := range refs { + if ref.RefAction == references.XRefActionCloses { + closeIssueIndexes = append(closeIssueIndexes, fmt.Sprintf("%s %s%d", closeWord, issueReference, ref.Issue.Index)) + } + } + if len(closeIssueIndexes) > 0 { + vars["ClosingIssues"] = strings.Join(closeIssueIndexes, ", ") + } else { + vars["ClosingIssues"] = "" + } + } + + return os.Expand(templateContent, func(s string) string { + return vars[s] + }), nil + } + } + + // Squash merge has a different from other styles. + if mergeStyle == repo_model.MergeStyleSquash { + return fmt.Sprintf("%s (%s%d)", pr.Issue.Title, issueReference, pr.Issue.Index), nil + } + + if pr.BaseRepoID == pr.HeadRepoID { + return fmt.Sprintf("Merge pull request '%s' (%s%d) from %s into %s", pr.Issue.Title, issueReference, pr.Issue.Index, pr.HeadBranch, pr.BaseBranch), nil + } + + if pr.HeadRepo == nil { + return fmt.Sprintf("Merge pull request '%s' (%s%d) from <deleted>:%s into %s", pr.Issue.Title, issueReference, pr.Issue.Index, pr.HeadBranch, pr.BaseBranch), nil + } + + return fmt.Sprintf("Merge pull request '%s' (%s%d) from %s:%s into %s", pr.Issue.Title, issueReference, pr.Issue.Index, pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseBranch), nil +} + // Merge merges pull request to base repository. // Caller should check PR is ready to be merged (review and status checks) -func Merge(pr *models.PullRequest, doer *user_model.User, baseGitRepo *git.Repository, mergeStyle repo_model.MergeStyle, expectedHeadCommitID, message string) error { +func Merge(ctx context.Context, pr *models.PullRequest, doer *user_model.User, baseGitRepo *git.Repository, mergeStyle repo_model.MergeStyle, expectedHeadCommitID, message string) error { if err := pr.LoadHeadRepo(); err != nil { log.Error("LoadHeadRepo: %v", err) return fmt.Errorf("LoadHeadRepo: %v", err) @@ -79,18 +172,18 @@ func Merge(pr *models.PullRequest, doer *user_model.User, baseGitRepo *git.Repos pr.Merger = doer pr.MergerID = doer.ID - if _, err := pr.SetMerged(db.DefaultContext); err != nil { + if _, err := pr.SetMerged(ctx); err != nil { log.Error("setMerged [%d]: %v", pr.ID, err) } - if err := pr.LoadIssueCtx(db.DefaultContext); err != nil { + if err := pr.LoadIssueCtx(ctx); err != nil { log.Error("loadIssue [%d]: %v", pr.ID, err) } - if err := pr.Issue.LoadRepo(db.DefaultContext); err != nil { + if err := pr.Issue.LoadRepo(ctx); err != nil { log.Error("loadRepo for issue [%d]: %v", pr.ID, err) } - if err := pr.Issue.Repo.GetOwner(db.DefaultContext); err != nil { + if err := pr.Issue.Repo.GetOwner(ctx); err != nil { log.Error("GetOwner for issue repo [%d]: %v", pr.ID, err) } @@ -100,17 +193,17 @@ func Merge(pr *models.PullRequest, doer *user_model.User, baseGitRepo *git.Repos cache.Remove(pr.Issue.Repo.GetCommitsCountCacheKey(pr.BaseBranch, true)) // Resolve cross references - refs, err := pr.ResolveCrossReferences(db.DefaultContext) + refs, err := pr.ResolveCrossReferences(ctx) if err != nil { log.Error("ResolveCrossReferences: %v", err) return nil } for _, ref := range refs { - if err = ref.LoadIssueCtx(db.DefaultContext); err != nil { + if err = ref.LoadIssueCtx(ctx); err != nil { return err } - if err = ref.Issue.LoadRepo(db.DefaultContext); err != nil { + if err = ref.Issue.LoadRepo(ctx); err != nil { return err } close := ref.RefAction == references.XRefActionCloses diff --git a/services/pull/pull_test.go b/services/pull/pull_test.go index 81627ebb77..09bae97780 100644 --- a/services/pull/pull_test.go +++ b/services/pull/pull_test.go @@ -8,6 +8,12 @@ package pull import ( "testing" + "code.gitea.io/gitea/models" + repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/models/unit" + "code.gitea.io/gitea/models/unittest" + "code.gitea.io/gitea/modules/git" + "github.com/stretchr/testify/assert" ) @@ -29,3 +35,57 @@ func TestPullRequest_CommitMessageTrailersPattern(t *testing.T) { assert.True(t, commitMessageTrailersPattern.MatchString("Additional whitespace is accepted.\n\nSigned-off-by \t : \tBob <bob@example.com> ")) assert.True(t, commitMessageTrailersPattern.MatchString("Folded value.\n\nFolded-trailer: This is\n a folded\n trailer value\nOther-Trailer: Value")) } + +func TestPullRequest_GetDefaultMergeMessage_InternalTracker(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + pr := unittest.AssertExistsAndLoadBean(t, &models.PullRequest{ID: 2}).(*models.PullRequest) + + assert.NoError(t, pr.LoadBaseRepo()) + gitRepo, err := git.OpenRepository(git.DefaultContext, pr.BaseRepo.RepoPath()) + assert.NoError(t, err) + defer gitRepo.Close() + + mergeMessage, err := GetDefaultMergeMessage(gitRepo, pr, "") + assert.NoError(t, err) + assert.Equal(t, "Merge pull request 'issue3' (#3) from branch2 into master", mergeMessage) + + pr.BaseRepoID = 1 + pr.HeadRepoID = 2 + mergeMessage, err = GetDefaultMergeMessage(gitRepo, pr, "") + assert.NoError(t, err) + assert.Equal(t, "Merge pull request 'issue3' (#3) from user2/repo1:branch2 into master", mergeMessage) +} + +func TestPullRequest_GetDefaultMergeMessage_ExternalTracker(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + + externalTracker := repo_model.RepoUnit{ + Type: unit.TypeExternalTracker, + Config: &repo_model.ExternalTrackerConfig{ + ExternalTrackerFormat: "https://someurl.com/{user}/{repo}/{issue}", + }, + } + baseRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) + baseRepo.Units = []*repo_model.RepoUnit{&externalTracker} + + pr := unittest.AssertExistsAndLoadBean(t, &models.PullRequest{ID: 2, BaseRepo: baseRepo}).(*models.PullRequest) + + assert.NoError(t, pr.LoadBaseRepo()) + gitRepo, err := git.OpenRepository(git.DefaultContext, pr.BaseRepo.RepoPath()) + assert.NoError(t, err) + defer gitRepo.Close() + + mergeMessage, err := GetDefaultMergeMessage(gitRepo, pr, "") + assert.NoError(t, err) + + assert.Equal(t, "Merge pull request 'issue3' (!3) from branch2 into master", mergeMessage) + + pr.BaseRepoID = 1 + pr.HeadRepoID = 2 + pr.BaseRepo = nil + pr.HeadRepo = nil + mergeMessage, err = GetDefaultMergeMessage(gitRepo, pr, "") + assert.NoError(t, err) + + assert.Equal(t, "Merge pull request 'issue3' (#3) from user2/repo2:branch2 into master", mergeMessage) +} |