Skip to content

Commit 39d51e7

Browse files
authored
Automerge supports deleting branch automatically after merging (#32343)
Resolve #32341 ~Depends on #27151~ - [x] It will display a checkbox of deleting the head branch on the pull request view page when starting an auto-merge task. - [x] Add permission check before deleting the branch - [x] Add delete branch comment for those closing pull requests because of head branch or base branch was deleted. - [x] Merge `RetargetChildrenOnMerge` and `AddDeletePRBranchComment` into `service.DeleteBranch`.
1 parent 2298ff2 commit 39d51e7

File tree

16 files changed

+149
-97
lines changed

16 files changed

+149
-97
lines changed

models/issues/pull_list.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,23 @@ func (prs PullRequestList) getRepositoryIDs() []int64 {
166166
return repoIDs.Values()
167167
}
168168

169+
func (prs PullRequestList) SetBaseRepo(baseRepo *repo_model.Repository) {
170+
for _, pr := range prs {
171+
if pr.BaseRepo == nil {
172+
pr.BaseRepo = baseRepo
173+
}
174+
}
175+
}
176+
177+
func (prs PullRequestList) SetHeadRepo(headRepo *repo_model.Repository) {
178+
for _, pr := range prs {
179+
if pr.HeadRepo == nil {
180+
pr.HeadRepo = headRepo
181+
pr.isHeadRepoLoaded = true
182+
}
183+
}
184+
}
185+
169186
func (prs PullRequestList) LoadRepositories(ctx context.Context) error {
170187
repoIDs := prs.getRepositoryIDs()
171188
reposMap := make(map[int64]*repo_model.Repository, len(repoIDs))

models/migrations/migrations.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"code.gitea.io/gitea/models/migrations/v1_21"
2323
"code.gitea.io/gitea/models/migrations/v1_22"
2424
"code.gitea.io/gitea/models/migrations/v1_23"
25+
"code.gitea.io/gitea/models/migrations/v1_24"
2526
"code.gitea.io/gitea/models/migrations/v1_6"
2627
"code.gitea.io/gitea/models/migrations/v1_7"
2728
"code.gitea.io/gitea/models/migrations/v1_8"
@@ -369,6 +370,9 @@ func prepareMigrationTasks() []*migration {
369370
newMigration(309, "Improve Notification table indices", v1_23.ImproveNotificationTableIndices),
370371
newMigration(310, "Add Priority to ProtectedBranch", v1_23.AddPriorityToProtectedBranch),
371372
newMigration(311, "Add TimeEstimate to Issue table", v1_23.AddTimeEstimateColumnToIssueTable),
373+
374+
// Gitea 1.23.0-rc0 ends at migration ID number 311 (database version 312)
375+
newMigration(312, "Add DeleteBranchAfterMerge to AutoMerge", v1_24.AddDeleteBranchAfterMergeForAutoMerge),
372376
}
373377
return preparedMigrations
374378
}

models/migrations/v1_24/v312.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright 2024 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package v1_24 //nolint
5+
6+
import (
7+
"xorm.io/xorm"
8+
)
9+
10+
type pullAutoMerge struct {
11+
DeleteBranchAfterMerge bool
12+
}
13+
14+
// TableName return database table name for xorm
15+
func (pullAutoMerge) TableName() string {
16+
return "pull_auto_merge"
17+
}
18+
19+
func AddDeleteBranchAfterMergeForAutoMerge(x *xorm.Engine) error {
20+
return x.Sync(new(pullAutoMerge))
21+
}

models/pull/automerge.go

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,14 @@ import (
1515

1616
// AutoMerge represents a pull request scheduled for merging when checks succeed
1717
type AutoMerge struct {
18-
ID int64 `xorm:"pk autoincr"`
19-
PullID int64 `xorm:"UNIQUE"`
20-
DoerID int64 `xorm:"INDEX NOT NULL"`
21-
Doer *user_model.User `xorm:"-"`
22-
MergeStyle repo_model.MergeStyle `xorm:"varchar(30)"`
23-
Message string `xorm:"LONGTEXT"`
24-
CreatedUnix timeutil.TimeStamp `xorm:"created"`
18+
ID int64 `xorm:"pk autoincr"`
19+
PullID int64 `xorm:"UNIQUE"`
20+
DoerID int64 `xorm:"INDEX NOT NULL"`
21+
Doer *user_model.User `xorm:"-"`
22+
MergeStyle repo_model.MergeStyle `xorm:"varchar(30)"`
23+
Message string `xorm:"LONGTEXT"`
24+
DeleteBranchAfterMerge bool
25+
CreatedUnix timeutil.TimeStamp `xorm:"created"`
2526
}
2627

2728
// TableName return database table name for xorm
@@ -49,7 +50,7 @@ func IsErrAlreadyScheduledToAutoMerge(err error) bool {
4950
}
5051

5152
// ScheduleAutoMerge schedules a pull request to be merged when all checks succeed
52-
func ScheduleAutoMerge(ctx context.Context, doer *user_model.User, pullID int64, style repo_model.MergeStyle, message string) error {
53+
func ScheduleAutoMerge(ctx context.Context, doer *user_model.User, pullID int64, style repo_model.MergeStyle, message string, deleteBranchAfterMerge bool) error {
5354
// Check if we already have a merge scheduled for that pull request
5455
if exists, _, err := GetScheduledMergeByPullID(ctx, pullID); err != nil {
5556
return err
@@ -58,10 +59,11 @@ func ScheduleAutoMerge(ctx context.Context, doer *user_model.User, pullID int64,
5859
}
5960

6061
_, err := db.GetEngine(ctx).Insert(&AutoMerge{
61-
DoerID: doer.ID,
62-
PullID: pullID,
63-
MergeStyle: style,
64-
Message: message,
62+
DoerID: doer.ID,
63+
PullID: pullID,
64+
MergeStyle: style,
65+
Message: message,
66+
DeleteBranchAfterMerge: deleteBranchAfterMerge,
6567
})
6668
return err
6769
}

routers/api/v1/repo/branch.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ func DeleteBranch(ctx *context.APIContext) {
150150
}
151151
}
152152

153-
if err := repo_service.DeleteBranch(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, branchName); err != nil {
153+
if err := repo_service.DeleteBranch(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, branchName, nil); err != nil {
154154
switch {
155155
case git.IsErrBranchNotExist(err):
156156
ctx.NotFound(err)

routers/api/v1/repo/pull.go

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -971,7 +971,7 @@ func MergePullRequest(ctx *context.APIContext) {
971971
}
972972

973973
if form.MergeWhenChecksSucceed {
974-
scheduled, err := automerge.ScheduleAutoMerge(ctx, ctx.Doer, pr, repo_model.MergeStyle(form.Do), message)
974+
scheduled, err := automerge.ScheduleAutoMerge(ctx, ctx.Doer, pr, repo_model.MergeStyle(form.Do), message, form.DeleteBranchAfterMerge)
975975
if err != nil {
976976
if pull_model.IsErrAlreadyScheduledToAutoMerge(err) {
977977
ctx.Error(http.StatusConflict, "ScheduleAutoMerge", err)
@@ -1043,11 +1043,8 @@ func MergePullRequest(ctx *context.APIContext) {
10431043
}
10441044
defer headRepo.Close()
10451045
}
1046-
if err := pull_service.RetargetChildrenOnMerge(ctx, ctx.Doer, pr); err != nil {
1047-
ctx.Error(http.StatusInternalServerError, "RetargetChildrenOnMerge", err)
1048-
return
1049-
}
1050-
if err := repo_service.DeleteBranch(ctx, ctx.Doer, pr.HeadRepo, headRepo, pr.HeadBranch); err != nil {
1046+
1047+
if err := repo_service.DeleteBranch(ctx, ctx.Doer, pr.HeadRepo, headRepo, pr.HeadBranch, pr); err != nil {
10511048
switch {
10521049
case git.IsErrBranchNotExist(err):
10531050
ctx.NotFound(err)
@@ -1060,10 +1057,6 @@ func MergePullRequest(ctx *context.APIContext) {
10601057
}
10611058
return
10621059
}
1063-
if err := issues_model.AddDeletePRBranchComment(ctx, ctx.Doer, pr.BaseRepo, pr.Issue.ID, pr.HeadBranch); err != nil {
1064-
// Do not fail here as branch has already been deleted
1065-
log.Error("DeleteBranch: %v", err)
1066-
}
10671060
}
10681061
}
10691062

routers/private/hook_post_receive_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ func TestHandlePullRequestMerging(t *testing.T) {
2727

2828
user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
2929

30-
err = pull_model.ScheduleAutoMerge(db.DefaultContext, user1, pr.ID, repo_model.MergeStyleSquash, "squash merge a pr")
30+
err = pull_model.ScheduleAutoMerge(db.DefaultContext, user1, pr.ID, repo_model.MergeStyleSquash, "squash merge a pr", false)
3131
assert.NoError(t, err)
3232

3333
autoMerge := unittest.AssertExistsAndLoadBean(t, &pull_model.AutoMerge{PullID: pr.ID})

routers/web/repo/branch.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ func DeleteBranchPost(ctx *context.Context) {
9797
defer redirect(ctx)
9898
branchName := ctx.FormString("name")
9999

100-
if err := repo_service.DeleteBranch(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, branchName); err != nil {
100+
if err := repo_service.DeleteBranch(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, branchName, nil); err != nil {
101101
switch {
102102
case git.IsErrBranchNotExist(err):
103103
log.Debug("DeleteBranch: Can't delete non existing branch '%s'", branchName)

routers/web/repo/pull.go

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1097,7 +1097,7 @@ func MergePullRequest(ctx *context.Context) {
10971097
// delete all scheduled auto merges
10981098
_ = pull_model.DeleteScheduledAutoMerge(ctx, pr.ID)
10991099
// schedule auto merge
1100-
scheduled, err := automerge.ScheduleAutoMerge(ctx, ctx.Doer, pr, repo_model.MergeStyle(form.Do), message)
1100+
scheduled, err := automerge.ScheduleAutoMerge(ctx, ctx.Doer, pr, repo_model.MergeStyle(form.Do), message, form.DeleteBranchAfterMerge)
11011101
if err != nil {
11021102
ctx.ServerError("ScheduleAutoMerge", err)
11031103
return
@@ -1504,12 +1504,7 @@ func CleanUpPullRequest(ctx *context.Context) {
15041504
func deleteBranch(ctx *context.Context, pr *issues_model.PullRequest, gitRepo *git.Repository) {
15051505
fullBranchName := pr.HeadRepo.FullName() + ":" + pr.HeadBranch
15061506

1507-
if err := pull_service.RetargetChildrenOnMerge(ctx, ctx.Doer, pr); err != nil {
1508-
ctx.Flash.Error(ctx.Tr("repo.branch.deletion_failed", fullBranchName))
1509-
return
1510-
}
1511-
1512-
if err := repo_service.DeleteBranch(ctx, ctx.Doer, pr.HeadRepo, gitRepo, pr.HeadBranch); err != nil {
1507+
if err := repo_service.DeleteBranch(ctx, ctx.Doer, pr.HeadRepo, gitRepo, pr.HeadBranch, pr); err != nil {
15131508
switch {
15141509
case git.IsErrBranchNotExist(err):
15151510
ctx.Flash.Error(ctx.Tr("repo.branch.deletion_failed", fullBranchName))
@@ -1524,11 +1519,6 @@ func deleteBranch(ctx *context.Context, pr *issues_model.PullRequest, gitRepo *g
15241519
return
15251520
}
15261521

1527-
if err := issues_model.AddDeletePRBranchComment(ctx, ctx.Doer, pr.BaseRepo, pr.IssueID, pr.HeadBranch); err != nil {
1528-
// Do not fail here as branch has already been deleted
1529-
log.Error("DeleteBranch: %v", err)
1530-
}
1531-
15321522
ctx.Flash.Success(ctx.Tr("repo.branch.deletion_success", fullBranchName))
15331523
}
15341524

services/automerge/automerge.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"code.gitea.io/gitea/modules/queue"
2525
notify_service "code.gitea.io/gitea/services/notify"
2626
pull_service "code.gitea.io/gitea/services/pull"
27+
repo_service "code.gitea.io/gitea/services/repository"
2728
)
2829

2930
// prAutoMergeQueue represents a queue to handle update pull request tests
@@ -63,9 +64,9 @@ func addToQueue(pr *issues_model.PullRequest, sha string) {
6364
}
6465

6566
// ScheduleAutoMerge if schedule is false and no error, pull can be merged directly
66-
func ScheduleAutoMerge(ctx context.Context, doer *user_model.User, pull *issues_model.PullRequest, style repo_model.MergeStyle, message string) (scheduled bool, err error) {
67+
func ScheduleAutoMerge(ctx context.Context, doer *user_model.User, pull *issues_model.PullRequest, style repo_model.MergeStyle, message string, deleteBranchAfterMerge bool) (scheduled bool, err error) {
6768
err = db.WithTx(ctx, func(ctx context.Context) error {
68-
if err := pull_model.ScheduleAutoMerge(ctx, doer, pull.ID, style, message); err != nil {
69+
if err := pull_model.ScheduleAutoMerge(ctx, doer, pull.ID, style, message, deleteBranchAfterMerge); err != nil {
6970
return err
7071
}
7172
scheduled = true
@@ -303,4 +304,10 @@ func handlePullRequestAutoMerge(pullID int64, sha string) {
303304
// on the pull request page. But this should not be finished in a bug fix PR which will be backport to release branch.
304305
return
305306
}
307+
308+
if pr.Flow == issues_model.PullRequestFlowGithub && scheduledPRM.DeleteBranchAfterMerge {
309+
if err := repo_service.DeleteBranch(ctx, doer, pr.HeadRepo, headGitRepo, pr.HeadBranch, pr); err != nil {
310+
log.Error("DeletePullRequestHeadBranch: %v", err)
311+
}
312+
}
306313
}

0 commit comments

Comments
 (0)