Skip to content

Commit 4344a64

Browse files
lunnyzeripath
andauthored
Allow custom default merge message with .gitea/default_merge_message/<merge_style>_TEMPLATE.md (go-gitea#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 <[email protected]> * Fix test * Fix test Co-authored-by: zeripath <[email protected]>
1 parent 5ca224a commit 4344a64

File tree

14 files changed

+292
-165
lines changed

14 files changed

+292
-165
lines changed

docs/content/doc/usage/issue-pull-request-templates.en-us.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,39 @@ Possible file names for PR templates:
4343
- `.github/PULL_REQUEST_TEMPLATE.md`
4444
- `.github/pull_request_template.md`
4545

46+
Possible file names for PR default merge message templates:
47+
48+
- `.gitea/default_merge_message/MERGE_TEMPLATE.md`
49+
- `.gitea/default_merge_message/REBASE_TEMPLATE.md`
50+
- `.gitea/default_merge_message/REBASE-MERGE_TEMPLATE.md`
51+
- `.gitea/default_merge_message/SQUASH_TEMPLATE.md`
52+
- `.gitea/default_merge_message/MANUALLY-MERGED_TEMPLATE.md`
53+
- `.gitea/default_merge_message/REBASE-UPDATE-ONLY_TEMPLATE.md`
54+
55+
Possible file names for PR default merge message templates:
56+
57+
- `.gitea/default_merge_message/MERGE_TEMPLATE.md`
58+
- `.gitea/default_merge_message/REBASE_TEMPLATE.md`
59+
- `.gitea/default_merge_message/REBASE-MERGE_TEMPLATE.md`
60+
- `.gitea/default_merge_message/SQUASH_TEMPLATE.md`
61+
- `.gitea/default_merge_message/MANUALLY-MERGED_TEMPLATE.md`
62+
- `.gitea/default_merge_message/REBASE-UPDATE-ONLY_TEMPLATE.md`
63+
64+
You can use the following variables enclosed in `${}` inside these templates which follow [os.Expand](https://pkg.go.dev/os#Expand) syntax:
65+
66+
- BaseRepoOwnerName: Base repository owner name of this pull request
67+
- BaseRepoName: Base repository name of this pull request
68+
- BaseBranch: Base repository target branch name of this pull request
69+
- HeadRepoOwnerName: Head repository owner name of this pull request
70+
- HeadRepoName: Head repository name of this pull request
71+
- HeadBranch: Head repository branch name of this pull request
72+
- PullRequestTitle: Pull request's title
73+
- PullRequestDescription: Pull request's description
74+
- PullRequestPosterName: Pull request's poster name
75+
- PullRequestIndex: Pull request's index number
76+
- PullRequestReference: Pull request's reference char with index number. i.e. #1, !2
77+
- ClosingIssues: return a string contains all issues which will be closed by this pull request i.e. `close #1, close #2`
78+
4679
Additionally, the New Issue page URL can be suffixed with `?title=Issue+Title&body=Issue+Text` and the form will be populated with those strings. Those strings will be used instead of the template if there is one.
4780

4881
## Issue Template Directory

integrations/pull_merge_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package integrations
66

77
import (
88
"bytes"
9+
"context"
910
"fmt"
1011
"net/http"
1112
"net/http/httptest"
@@ -243,11 +244,11 @@ func TestCantMergeConflict(t *testing.T) {
243244
gitRepo, err := git.OpenRepository(git.DefaultContext, repo_model.RepoPath(user1.Name, repo1.Name))
244245
assert.NoError(t, err)
245246

246-
err = pull.Merge(pr, user1, gitRepo, repo_model.MergeStyleMerge, "", "CONFLICT")
247+
err = pull.Merge(context.Background(), pr, user1, gitRepo, repo_model.MergeStyleMerge, "", "CONFLICT")
247248
assert.Error(t, err, "Merge should return an error due to conflict")
248249
assert.True(t, models.IsErrMergeConflicts(err), "Merge error is not a conflict error")
249250

250-
err = pull.Merge(pr, user1, gitRepo, repo_model.MergeStyleRebase, "", "CONFLICT")
251+
err = pull.Merge(context.Background(), pr, user1, gitRepo, repo_model.MergeStyleRebase, "", "CONFLICT")
251252
assert.Error(t, err, "Merge should return an error due to conflict")
252253
assert.True(t, models.IsErrRebaseConflicts(err), "Merge error is not a conflict error")
253254
gitRepo.Close()
@@ -342,7 +343,7 @@ func TestCantMergeUnrelated(t *testing.T) {
342343
BaseBranch: "base",
343344
}).(*models.PullRequest)
344345

345-
err = pull.Merge(pr, user1, gitRepo, repo_model.MergeStyleMerge, "", "UNRELATED")
346+
err = pull.Merge(context.Background(), pr, user1, gitRepo, repo_model.MergeStyleMerge, "", "UNRELATED")
346347
assert.Error(t, err, "Merge should return an error due to unrelated")
347348
assert.True(t, models.IsErrMergeUnrelatedHistories(err), "Merge error is not a unrelated histories error")
348349
gitRepo.Close()

models/pull.go

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import (
1313

1414
"code.gitea.io/gitea/models/db"
1515
repo_model "code.gitea.io/gitea/models/repo"
16-
"code.gitea.io/gitea/models/unit"
1716
user_model "code.gitea.io/gitea/models/user"
1817
"code.gitea.io/gitea/modules/git"
1918
"code.gitea.io/gitea/modules/log"
@@ -228,34 +227,6 @@ func (pr *PullRequest) LoadProtectedBranchCtx(ctx context.Context) (err error) {
228227
return
229228
}
230229

231-
// GetDefaultMergeMessage returns default message used when merging pull request
232-
func (pr *PullRequest) GetDefaultMergeMessage(ctx context.Context) (string, error) {
233-
if pr.HeadRepo == nil {
234-
var err error
235-
pr.HeadRepo, err = repo_model.GetRepositoryByIDCtx(ctx, pr.HeadRepoID)
236-
if err != nil {
237-
return "", fmt.Errorf("GetRepositoryById[%d]: %v", pr.HeadRepoID, err)
238-
}
239-
}
240-
if err := pr.LoadIssueCtx(ctx); err != nil {
241-
return "", fmt.Errorf("Cannot load issue %d for PR id %d: Error: %v", pr.IssueID, pr.ID, err)
242-
}
243-
if err := pr.LoadBaseRepoCtx(ctx); err != nil {
244-
return "", fmt.Errorf("LoadBaseRepo: %v", err)
245-
}
246-
247-
issueReference := "#"
248-
if pr.BaseRepo.UnitEnabledCtx(ctx, unit.TypeExternalTracker) {
249-
issueReference = "!"
250-
}
251-
252-
if pr.BaseRepoID == pr.HeadRepoID {
253-
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
254-
}
255-
256-
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
257-
}
258-
259230
// ReviewCount represents a count of Reviews
260231
type ReviewCount struct {
261232
IssueID int64
@@ -338,20 +309,6 @@ func (pr *PullRequest) getReviewedByLines(writer io.Writer) error {
338309
return committer.Commit()
339310
}
340311

341-
// GetDefaultSquashMessage returns default message used when squash and merging pull request
342-
func (pr *PullRequest) GetDefaultSquashMessage(ctx context.Context) (string, error) {
343-
if err := pr.LoadIssueCtx(ctx); err != nil {
344-
return "", fmt.Errorf("LoadIssue: %v", err)
345-
}
346-
if err := pr.LoadBaseRepoCtx(ctx); err != nil {
347-
return "", fmt.Errorf("LoadBaseRepo: %v", err)
348-
}
349-
if pr.BaseRepo.UnitEnabledCtx(ctx, unit.TypeExternalTracker) {
350-
return fmt.Sprintf("%s (!%d)", pr.Issue.Title, pr.Issue.Index), nil
351-
}
352-
return fmt.Sprintf("%s (#%d)", pr.Issue.Title, pr.Issue.Index), nil
353-
}
354-
355312
// GetGitRefName returns git ref for hidden pull request branch
356313
func (pr *PullRequest) GetGitRefName() string {
357314
return fmt.Sprintf("%s%d/head", git.PullPrefix, pr.Index)

models/pull_test.go

Lines changed: 0 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,7 @@ import (
88
"testing"
99

1010
"code.gitea.io/gitea/models/db"
11-
repo_model "code.gitea.io/gitea/models/repo"
12-
"code.gitea.io/gitea/models/unit"
1311
"code.gitea.io/gitea/models/unittest"
14-
user_model "code.gitea.io/gitea/models/user"
1512

1613
"github.com/stretchr/testify/assert"
1714
)
@@ -256,53 +253,3 @@ func TestPullRequest_GetWorkInProgressPrefixWorkInProgress(t *testing.T) {
256253
pr.Issue.Title = "[wip] " + original
257254
assert.Equal(t, "[wip]", pr.GetWorkInProgressPrefix())
258255
}
259-
260-
func TestPullRequest_GetDefaultMergeMessage_InternalTracker(t *testing.T) {
261-
assert.NoError(t, unittest.PrepareTestDatabase())
262-
pr := unittest.AssertExistsAndLoadBean(t, &PullRequest{ID: 2}).(*PullRequest)
263-
264-
msg, err := pr.GetDefaultMergeMessage(db.DefaultContext)
265-
assert.NoError(t, err)
266-
assert.Equal(t, "Merge pull request 'issue3' (#3) from branch2 into master", msg)
267-
268-
pr.BaseRepoID = 1
269-
pr.HeadRepoID = 2
270-
msg, err = pr.GetDefaultMergeMessage(db.DefaultContext)
271-
assert.NoError(t, err)
272-
assert.Equal(t, "Merge pull request 'issue3' (#3) from user2/repo1:branch2 into master", msg)
273-
}
274-
275-
func TestPullRequest_GetDefaultMergeMessage_ExternalTracker(t *testing.T) {
276-
assert.NoError(t, unittest.PrepareTestDatabase())
277-
278-
externalTracker := repo_model.RepoUnit{
279-
Type: unit.TypeExternalTracker,
280-
Config: &repo_model.ExternalTrackerConfig{
281-
ExternalTrackerFormat: "https://someurl.com/{user}/{repo}/{issue}",
282-
},
283-
}
284-
baseRepo := &repo_model.Repository{Name: "testRepo", ID: 1}
285-
baseRepo.Owner = &user_model.User{Name: "testOwner"}
286-
baseRepo.Units = []*repo_model.RepoUnit{&externalTracker}
287-
288-
pr := unittest.AssertExistsAndLoadBean(t, &PullRequest{ID: 2, BaseRepo: baseRepo}).(*PullRequest)
289-
290-
msg, err := pr.GetDefaultMergeMessage(db.DefaultContext)
291-
assert.NoError(t, err)
292-
assert.Equal(t, "Merge pull request 'issue3' (!3) from branch2 into master", msg)
293-
294-
pr.BaseRepoID = 1
295-
pr.HeadRepoID = 2
296-
msg, err = pr.GetDefaultMergeMessage(db.DefaultContext)
297-
assert.NoError(t, err)
298-
assert.Equal(t, "Merge pull request 'issue3' (!3) from user2/repo1:branch2 into master", msg)
299-
}
300-
301-
func TestPullRequest_GetDefaultSquashMessage(t *testing.T) {
302-
assert.NoError(t, unittest.PrepareTestDatabase())
303-
pr := unittest.AssertExistsAndLoadBean(t, &PullRequest{ID: 2}).(*PullRequest)
304-
305-
msg, err := pr.GetDefaultSquashMessage(db.DefaultContext)
306-
assert.NoError(t, err)
307-
assert.Equal(t, "issue3 (#3)", msg)
308-
}

models/repo/repo_unit.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -173,8 +173,6 @@ func (r *RepoUnit) BeforeSet(colName string, val xorm.Cell) {
173173
switch colName {
174174
case "type":
175175
switch unit.Type(db.Cell2Int64(val)) {
176-
case unit.TypeCode, unit.TypeReleases, unit.TypeWiki, unit.TypeProjects:
177-
r.Config = new(UnitConfig)
178176
case unit.TypeExternalWiki:
179177
r.Config = new(ExternalWikiConfig)
180178
case unit.TypeExternalTracker:
@@ -183,8 +181,10 @@ func (r *RepoUnit) BeforeSet(colName string, val xorm.Cell) {
183181
r.Config = new(PullRequestsConfig)
184182
case unit.TypeIssues:
185183
r.Config = new(IssuesConfig)
184+
case unit.TypeCode, unit.TypeReleases, unit.TypeWiki, unit.TypeProjects:
185+
fallthrough
186186
default:
187-
panic(fmt.Sprintf("unrecognized repo unit type: %v", *val))
187+
r.Config = new(UnitConfig)
188188
}
189189
}
190190
}

modules/git/commit.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"strings"
1818

1919
"code.gitea.io/gitea/modules/log"
20+
"code.gitea.io/gitea/modules/util"
2021
)
2122

2223
// Commit represents a git commit.
@@ -306,6 +307,35 @@ func (c *Commit) HasFile(filename string) (bool, error) {
306307
return true, nil
307308
}
308309

310+
// GetFileContent reads a file content as a string or returns false if this was not possible
311+
func (c *Commit) GetFileContent(filename string, limit int) (string, error) {
312+
entry, err := c.GetTreeEntryByPath(filename)
313+
if err != nil {
314+
return "", err
315+
}
316+
317+
r, err := entry.Blob().DataAsync()
318+
if err != nil {
319+
return "", err
320+
}
321+
defer r.Close()
322+
323+
if limit > 0 {
324+
bs := make([]byte, limit)
325+
n, err := util.ReadAtMost(r, bs)
326+
if err != nil {
327+
return "", err
328+
}
329+
return string(bs[:n]), nil
330+
}
331+
332+
bytes, err := io.ReadAll(r)
333+
if err != nil {
334+
return "", err
335+
}
336+
return string(bytes), nil
337+
}
338+
309339
// GetSubModules get all the sub modules of current revision git tree
310340
func (c *Commit) GetSubModules() (*ObjectCache, error) {
311341
if c.submoduleCache != nil {

routers/api/v1/repo/pull.go

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -801,14 +801,26 @@ func MergePullRequest(ctx *context.APIContext) {
801801
return
802802
}
803803

804-
// set defaults to propagate needed fields
805-
if err := form.SetDefaults(ctx, pr); err != nil {
806-
ctx.ServerError("SetDefaults", fmt.Errorf("SetDefaults: %v", err))
807-
return
804+
if len(form.Do) == 0 {
805+
form.Do = string(repo_model.MergeStyleMerge)
806+
}
807+
808+
message := strings.TrimSpace(form.MergeTitleField)
809+
if len(message) == 0 {
810+
message, err = pull_service.GetDefaultMergeMessage(ctx.Repo.GitRepo, pr, repo_model.MergeStyle(form.Do))
811+
if err != nil {
812+
ctx.Error(http.StatusInternalServerError, "GetDefaultMergeMessage", err)
813+
return
814+
}
815+
}
816+
817+
form.MergeMessageField = strings.TrimSpace(form.MergeMessageField)
818+
if len(form.MergeMessageField) > 0 {
819+
message += "\n\n" + form.MergeMessageField
808820
}
809821

810822
if form.MergeWhenChecksSucceed {
811-
scheduled, err := automerge.ScheduleAutoMerge(ctx, ctx.Doer, pr, repo_model.MergeStyle(form.Do), form.MergeTitleField)
823+
scheduled, err := automerge.ScheduleAutoMerge(ctx, ctx.Doer, pr, repo_model.MergeStyle(form.Do), message)
812824
if err != nil {
813825
if pull_model.IsErrAlreadyScheduledToAutoMerge(err) {
814826
ctx.Error(http.StatusConflict, "ScheduleAutoMerge", err)
@@ -823,7 +835,7 @@ func MergePullRequest(ctx *context.APIContext) {
823835
}
824836
}
825837

826-
if err := pull_service.Merge(pr, ctx.Doer, ctx.Repo.GitRepo, repo_model.MergeStyle(form.Do), form.HeadCommitID, form.MergeTitleField); err != nil {
838+
if err := pull_service.Merge(ctx, pr, ctx.Doer, ctx.Repo.GitRepo, repo_model.MergeStyle(form.Do), form.HeadCommitID, message); err != nil {
827839
if models.IsErrInvalidMergeStyle(err) {
828840
ctx.Error(http.StatusMethodNotAllowed, "Invalid merge style", fmt.Errorf("%s is not allowed an allowed merge style for this repository", repo_model.MergeStyle(form.Do)))
829841
} else if models.IsErrMergeConflicts(err) {

routers/web/repo/issue.go

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -712,8 +712,6 @@ func RetrieveRepoMetas(ctx *context.Context, repo *repo_model.Repository, isPull
712712
}
713713

714714
func getFileContentFromDefaultBranch(ctx *context.Context, filename string) (string, bool) {
715-
var bytes []byte
716-
717715
if ctx.Repo.Commit == nil {
718716
var err error
719717
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch)
@@ -734,7 +732,7 @@ func getFileContentFromDefaultBranch(ctx *context.Context, filename string) (str
734732
return "", false
735733
}
736734
defer r.Close()
737-
bytes, err = io.ReadAll(r)
735+
bytes, err := io.ReadAll(r)
738736
if err != nil {
739737
return "", false
740738
}
@@ -1574,26 +1572,42 @@ func ViewIssue(ctx *context.Context) {
15741572
}
15751573
prConfig := prUnit.PullRequestsConfig()
15761574

1575+
var mergeStyle repo_model.MergeStyle
15771576
// Check correct values and select default
15781577
if ms, ok := ctx.Data["MergeStyle"].(repo_model.MergeStyle); !ok ||
15791578
!prConfig.IsMergeStyleAllowed(ms) {
15801579
defaultMergeStyle := prConfig.GetDefaultMergeStyle()
15811580
if prConfig.IsMergeStyleAllowed(defaultMergeStyle) && !ok {
1582-
ctx.Data["MergeStyle"] = defaultMergeStyle
1581+
mergeStyle = defaultMergeStyle
15831582
} else if prConfig.AllowMerge {
1584-
ctx.Data["MergeStyle"] = repo_model.MergeStyleMerge
1583+
mergeStyle = repo_model.MergeStyleMerge
15851584
} else if prConfig.AllowRebase {
1586-
ctx.Data["MergeStyle"] = repo_model.MergeStyleRebase
1585+
mergeStyle = repo_model.MergeStyleRebase
15871586
} else if prConfig.AllowRebaseMerge {
1588-
ctx.Data["MergeStyle"] = repo_model.MergeStyleRebaseMerge
1587+
mergeStyle = repo_model.MergeStyleRebaseMerge
15891588
} else if prConfig.AllowSquash {
1590-
ctx.Data["MergeStyle"] = repo_model.MergeStyleSquash
1589+
mergeStyle = repo_model.MergeStyleSquash
15911590
} else if prConfig.AllowManualMerge {
1592-
ctx.Data["MergeStyle"] = repo_model.MergeStyleManuallyMerged
1593-
} else {
1594-
ctx.Data["MergeStyle"] = ""
1591+
mergeStyle = repo_model.MergeStyleManuallyMerged
15951592
}
15961593
}
1594+
1595+
ctx.Data["MergeStyle"] = mergeStyle
1596+
1597+
defaultMergeMessage, err := pull_service.GetDefaultMergeMessage(ctx.Repo.GitRepo, pull, mergeStyle)
1598+
if err != nil {
1599+
ctx.ServerError("GetDefaultMergeMessage", err)
1600+
return
1601+
}
1602+
ctx.Data["DefaultMergeMessage"] = defaultMergeMessage
1603+
1604+
defaultSquashMergeMessage, err := pull_service.GetDefaultMergeMessage(ctx.Repo.GitRepo, pull, repo_model.MergeStyleSquash)
1605+
if err != nil {
1606+
ctx.ServerError("GetDefaultSquashMergeMessage", err)
1607+
return
1608+
}
1609+
ctx.Data["DefaultSquashMergeMessage"] = defaultSquashMergeMessage
1610+
15971611
if err = pull.LoadProtectedBranch(); err != nil {
15981612
ctx.ServerError("LoadProtectedBranch", err)
15991613
return

0 commit comments

Comments
 (0)