mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 21:28:11 +09:00 
			
		
		
		
	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>
This commit is contained in:
		| @@ -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. | ||||||
|   | |||||||
							
								
								
									
										67
									
								
								services/pull/merge_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								services/pull/merge_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -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, | ||||||
| 									}, | 									}, | ||||||
| 									{ | 									{ | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user