Support template for merge message description (#22248)

Fix #21435.

Use the first line of the template as the git commit message title, and
the rest as the description.

## Snapshots

<img width="806" alt="image"
src="https://user-images.githubusercontent.com/9418365/209644083-5d85179c-cf58-404f-bc98-c662398a2411.png">
<img width="860" alt="image"
src="https://user-images.githubusercontent.com/9418365/209644392-22573090-e2c1-458b-ba44-855b79735632.png">
<img width="1154" alt="image"
src="https://user-images.githubusercontent.com/9418365/209644457-a1b2711a-6787-45b4-b52c-a88d7fc132d7.png">

Co-authored-by: delvh <dev.lh@web.de>
pull/22146/head^2
Jason Song 2 years ago committed by GitHub
parent a35749893b
commit 47efba78ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      routers/api/v1/repo/pull.go
  2. 6
      routers/web/repo/issue.go
  3. 2
      routers/web/repo/pull.go
  4. 38
      services/pull/merge.go
  5. 67
      services/pull/merge_test.go
  6. 8
      services/pull/pull_test.go
  7. 5
      templates/repo/issue/view_content/pull.tmpl

@ -815,7 +815,7 @@ func MergePullRequest(ctx *context.APIContext) {
message := strings.TrimSpace(form.MergeTitleField) message := strings.TrimSpace(form.MergeTitleField)
if len(message) == 0 { if len(message) == 0 {
message, err = pull_service.GetDefaultMergeMessage(ctx, ctx.Repo.GitRepo, pr, repo_model.MergeStyle(form.Do)) message, _, err = pull_service.GetDefaultMergeMessage(ctx, ctx.Repo.GitRepo, pr, repo_model.MergeStyle(form.Do))
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "GetDefaultMergeMessage", err) ctx.Error(http.StatusInternalServerError, "GetDefaultMergeMessage", err)
return return

@ -1664,19 +1664,21 @@ func ViewIssue(ctx *context.Context) {
ctx.Data["MergeStyle"] = mergeStyle ctx.Data["MergeStyle"] = mergeStyle
defaultMergeMessage, err := pull_service.GetDefaultMergeMessage(ctx, ctx.Repo.GitRepo, pull, mergeStyle) defaultMergeMessage, defaultMergeBody, err := pull_service.GetDefaultMergeMessage(ctx, ctx.Repo.GitRepo, pull, mergeStyle)
if err != nil { if err != nil {
ctx.ServerError("GetDefaultMergeMessage", err) ctx.ServerError("GetDefaultMergeMessage", err)
return return
} }
ctx.Data["DefaultMergeMessage"] = defaultMergeMessage ctx.Data["DefaultMergeMessage"] = defaultMergeMessage
ctx.Data["DefaultMergeBody"] = defaultMergeBody
defaultSquashMergeMessage, err := pull_service.GetDefaultMergeMessage(ctx, ctx.Repo.GitRepo, pull, repo_model.MergeStyleSquash) defaultSquashMergeMessage, defaultSquashMergeBody, err := pull_service.GetDefaultMergeMessage(ctx, ctx.Repo.GitRepo, pull, repo_model.MergeStyleSquash)
if err != nil { if err != nil {
ctx.ServerError("GetDefaultSquashMergeMessage", err) ctx.ServerError("GetDefaultSquashMergeMessage", err)
return return
} }
ctx.Data["DefaultSquashMergeMessage"] = defaultSquashMergeMessage ctx.Data["DefaultSquashMergeMessage"] = defaultSquashMergeMessage
ctx.Data["DefaultSquashMergeBody"] = defaultSquashMergeBody
if err = pull.LoadProtectedBranch(ctx); err != nil { if err = pull.LoadProtectedBranch(ctx); err != nil {
ctx.ServerError("LoadProtectedBranch", err) ctx.ServerError("LoadProtectedBranch", err)

@ -986,7 +986,7 @@ func MergePullRequest(ctx *context.Context) {
message := strings.TrimSpace(form.MergeTitleField) message := strings.TrimSpace(form.MergeTitleField)
if len(message) == 0 { if len(message) == 0 {
var err error var err error
message, err = pull_service.GetDefaultMergeMessage(ctx, ctx.Repo.GitRepo, pr, repo_model.MergeStyle(form.Do)) message, _, err = pull_service.GetDefaultMergeMessage(ctx, ctx.Repo.GitRepo, pr, repo_model.MergeStyle(form.Do))
if err != nil { if err != nil {
ctx.ServerError("GetDefaultMergeMessage", err) ctx.ServerError("GetDefaultMergeMessage", err)
return return

@ -39,19 +39,19 @@ import (
) )
// GetDefaultMergeMessage returns default message used when merging pull request // GetDefaultMergeMessage returns default message used when merging pull request
func GetDefaultMergeMessage(ctx context.Context, baseGitRepo *git.Repository, pr *issues_model.PullRequest, mergeStyle repo_model.MergeStyle) (string, error) { func GetDefaultMergeMessage(ctx context.Context, baseGitRepo *git.Repository, pr *issues_model.PullRequest, mergeStyle repo_model.MergeStyle) (message, body string, err error) {
if err := pr.LoadHeadRepo(ctx); err != nil { if err := pr.LoadHeadRepo(ctx); err != nil {
return "", err return "", "", err
} }
if err := pr.LoadBaseRepo(ctx); err != nil { if err := pr.LoadBaseRepo(ctx); err != nil {
return "", err return "", "", err
} }
if pr.BaseRepo == nil { if pr.BaseRepo == nil {
return "", repo_model.ErrRepoNotExist{ID: pr.BaseRepoID} return "", "", repo_model.ErrRepoNotExist{ID: pr.BaseRepoID}
} }
if err := pr.LoadIssue(ctx); err != nil { if err := pr.LoadIssue(ctx); err != nil {
return "", err return "", "", err
} }
isExternalTracker := pr.BaseRepo.UnitEnabled(ctx, unit.TypeExternalTracker) isExternalTracker := pr.BaseRepo.UnitEnabled(ctx, unit.TypeExternalTracker)
@ -64,12 +64,12 @@ func GetDefaultMergeMessage(ctx context.Context, baseGitRepo *git.Repository, pr
templateFilepath := fmt.Sprintf(".gitea/default_merge_message/%s_TEMPLATE.md", strings.ToUpper(string(mergeStyle))) templateFilepath := fmt.Sprintf(".gitea/default_merge_message/%s_TEMPLATE.md", strings.ToUpper(string(mergeStyle)))
commit, err := baseGitRepo.GetBranchCommit(pr.BaseRepo.DefaultBranch) commit, err := baseGitRepo.GetBranchCommit(pr.BaseRepo.DefaultBranch)
if err != nil { if err != nil {
return "", err return "", "", err
} }
templateContent, err := commit.GetFileContent(templateFilepath, setting.Repository.PullRequest.DefaultMergeMessageSize) templateContent, err := commit.GetFileContent(templateFilepath, setting.Repository.PullRequest.DefaultMergeMessageSize)
if err != nil { if err != nil {
if !git.IsErrNotExist(err) { if !git.IsErrNotExist(err) {
return "", err return "", "", err
} }
} else { } else {
vars := map[string]string{ vars := map[string]string{
@ -107,27 +107,35 @@ func GetDefaultMergeMessage(ctx context.Context, baseGitRepo *git.Repository, pr
vars["ClosingIssues"] = "" vars["ClosingIssues"] = ""
} }
} }
message, body = expandDefaultMergeMessage(templateContent, vars)
return os.Expand(templateContent, func(s string) string { return message, body, nil
return vars[s]
}), nil
} }
} }
// Squash merge has a different from other styles. // Squash merge has a different from other styles.
if mergeStyle == repo_model.MergeStyleSquash { if mergeStyle == repo_model.MergeStyleSquash {
return fmt.Sprintf("%s (%s%d)", pr.Issue.Title, issueReference, pr.Issue.Index), nil return fmt.Sprintf("%s (%s%d)", pr.Issue.Title, issueReference, pr.Issue.Index), "", nil
} }
if pr.BaseRepoID == pr.HeadRepoID { 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 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 { 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 <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 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
}
func expandDefaultMergeMessage(template string, vars map[string]string) (message, body string) {
message = strings.TrimSpace(template)
if splits := strings.SplitN(message, "\n", 2); len(splits) == 2 {
message = splits[0]
body = strings.TrimSpace(splits[1])
}
mapping := func(s string) string { return vars[s] }
return os.Expand(message, mapping), os.Expand(body, mapping)
} }
// Merge merges pull request to base repository. // Merge merges pull request to base repository.

@ -0,0 +1,67 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package pull
import (
"testing"
"github.com/stretchr/testify/assert"
)
func Test_expandDefaultMergeMessage(t *testing.T) {
type args struct {
template string
vars map[string]string
}
tests := []struct {
name string
args args
want string
wantBody string
}{
{
name: "single line",
args: args{
template: "Merge ${PullRequestTitle}",
vars: map[string]string{
"PullRequestTitle": "PullRequestTitle",
"PullRequestDescription": "Pull\nRequest\nDescription\n",
},
},
want: "Merge PullRequestTitle",
wantBody: "",
},
{
name: "multiple lines",
args: args{
template: "Merge ${PullRequestTitle}\nDescription:\n\n${PullRequestDescription}\n",
vars: map[string]string{
"PullRequestTitle": "PullRequestTitle",
"PullRequestDescription": "Pull\nRequest\nDescription\n",
},
},
want: "Merge PullRequestTitle",
wantBody: "Description:\n\nPull\nRequest\nDescription\n",
},
{
name: "leading newlines",
args: args{
template: "\n\n\nMerge ${PullRequestTitle}\n\n\nDescription:\n\n${PullRequestDescription}\n",
vars: map[string]string{
"PullRequestTitle": "PullRequestTitle",
"PullRequestDescription": "Pull\nRequest\nDescription\n",
},
},
want: "Merge PullRequestTitle",
wantBody: "Description:\n\nPull\nRequest\nDescription\n",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, got1 := expandDefaultMergeMessage(tt.args.template, tt.args.vars)
assert.Equalf(t, tt.want, got, "expandDefaultMergeMessage(%v, %v)", tt.args.template, tt.args.vars)
assert.Equalf(t, tt.wantBody, got1, "expandDefaultMergeMessage(%v, %v)", tt.args.template, tt.args.vars)
})
}
}

@ -45,13 +45,13 @@ func TestPullRequest_GetDefaultMergeMessage_InternalTracker(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
defer gitRepo.Close() defer gitRepo.Close()
mergeMessage, err := GetDefaultMergeMessage(db.DefaultContext, gitRepo, pr, "") mergeMessage, _, err := GetDefaultMergeMessage(db.DefaultContext, gitRepo, pr, "")
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, "Merge pull request 'issue3' (#3) from branch2 into master", mergeMessage) assert.Equal(t, "Merge pull request 'issue3' (#3) from branch2 into master", mergeMessage)
pr.BaseRepoID = 1 pr.BaseRepoID = 1
pr.HeadRepoID = 2 pr.HeadRepoID = 2
mergeMessage, err = GetDefaultMergeMessage(db.DefaultContext, gitRepo, pr, "") mergeMessage, _, err = GetDefaultMergeMessage(db.DefaultContext, gitRepo, pr, "")
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, "Merge pull request 'issue3' (#3) from user2/repo1:branch2 into master", mergeMessage) assert.Equal(t, "Merge pull request 'issue3' (#3) from user2/repo1:branch2 into master", mergeMessage)
} }
@ -75,7 +75,7 @@ func TestPullRequest_GetDefaultMergeMessage_ExternalTracker(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
defer gitRepo.Close() defer gitRepo.Close()
mergeMessage, err := GetDefaultMergeMessage(db.DefaultContext, gitRepo, pr, "") mergeMessage, _, err := GetDefaultMergeMessage(db.DefaultContext, gitRepo, pr, "")
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, "Merge pull request 'issue3' (!3) from branch2 into master", mergeMessage) assert.Equal(t, "Merge pull request 'issue3' (!3) from branch2 into master", mergeMessage)
@ -84,7 +84,7 @@ func TestPullRequest_GetDefaultMergeMessage_ExternalTracker(t *testing.T) {
pr.HeadRepoID = 2 pr.HeadRepoID = 2
pr.BaseRepo = nil pr.BaseRepo = nil
pr.HeadRepo = nil pr.HeadRepo = nil
mergeMessage, err = GetDefaultMergeMessage(db.DefaultContext, gitRepo, pr, "") mergeMessage, _, err = GetDefaultMergeMessage(db.DefaultContext, gitRepo, pr, "")
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, "Merge pull request 'issue3' (#3) from user2/repo2:branch2 into master", mergeMessage) assert.Equal(t, "Merge pull request 'issue3' (#3) from user2/repo2:branch2 into master", mergeMessage)

@ -343,7 +343,8 @@
(() => { (() => {
const defaultMergeTitle = {{.DefaultMergeMessage}}; const defaultMergeTitle = {{.DefaultMergeMessage}};
const defaultSquashMergeTitle = {{.DefaultSquashMergeMessage}}; const defaultSquashMergeTitle = {{.DefaultSquashMergeMessage}};
const defaultMergeMessage = 'Reviewed-on: ' + {{$.Issue.HTMLURL}} + '\n' + {{$approvers}}; const defaultMergeMessage = {{if .DefaultMergeBody}}{{.DefaultMergeBody}}{{else}}'Reviewed-on: ' + {{$.Issue.HTMLURL}} + '\n' + {{$approvers}}{{end}};
const defaultSquashMergeMessage = {{if .DefaultSquashMergeBody}}{{.DefaultSquashMergeBody}}{{else}}'Reviewed-on: ' + {{$.Issue.HTMLURL}} + '\n' + {{$approvers}}{{end}};
const mergeForm = { const mergeForm = {
'baseLink': {{.Link}}, 'baseLink': {{.Link}},
'textCancel': {{$.locale.Tr "cancel"}}, 'textCancel': {{$.locale.Tr "cancel"}},
@ -398,7 +399,7 @@
'allowed': {{$prUnit.PullRequestsConfig.AllowSquash}}, 'allowed': {{$prUnit.PullRequestsConfig.AllowSquash}},
'textDoMerge': {{$.locale.Tr "repo.pulls.squash_merge_pull_request"}}, 'textDoMerge': {{$.locale.Tr "repo.pulls.squash_merge_pull_request"}},
'mergeTitleFieldText': defaultSquashMergeTitle, 'mergeTitleFieldText': defaultSquashMergeTitle,
'mergeMessageFieldText': {{.GetCommitMessages}} + defaultMergeMessage, 'mergeMessageFieldText': {{.GetCommitMessages}} + defaultSquashMergeMessage,
'hideAutoMerge': generalHideAutoMerge, 'hideAutoMerge': generalHideAutoMerge,
}, },
{ {

Loading…
Cancel
Save