Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 8 additions & 62 deletions routers/web/repo/issue_view.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
"code.gitea.io/gitea/models/organization"
access_model "code.gitea.io/gitea/models/perm/access"
project_model "code.gitea.io/gitea/models/project"
pull_model "code.gitea.io/gitea/models/pull"
"code.gitea.io/gitea/models/renderhelper"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
Expand Down Expand Up @@ -826,6 +825,7 @@ func (prInfo *pullRequestViewInfo) prepareMergeBox(ctx *context.Context, issue *
panic("impossible, issue must be the same")
}

pull := issue.PullRequest
data := &pullMergeBoxData{}
prInfo.MergeBoxData = data

Expand All @@ -834,22 +834,19 @@ func (prInfo *pullRequestViewInfo) prepareMergeBox(ctx *context.Context, issue *
statusCheckData = &pullCommitStatusCheckData{} // make the following logic easier, no need to keep checking "nil"
}

pull := issue.PullRequest
canDelete := false
allowMerge := false
canWriteToHeadRepo := false

pull_service.StartPullRequestCheckOnView(ctx, pull)

ctx.Data["GetCommitMessages"] = ""
if !prInfo.IsPullRequestBroken {
var err error
ctx.Data["UpdateAllowed"], ctx.Data["UpdateByRebaseAllowed"], err = pull_service.IsUserAllowedToUpdate(ctx, pull, ctx.Doer)
if err != nil {
ctx.ServerError("IsUserAllowedToUpdate", err)
return
}
ctx.Data["GetCommitMessages"] = pull_service.GetSquashMergeCommitMessages(ctx, pull)
}

if pull.IsFilesConflicted() {
Expand Down Expand Up @@ -903,59 +900,11 @@ func (prInfo *pullRequestViewInfo) prepareMergeBox(ctx *context.Context, issue *
}
}

data.ReloadingInterval = util.Iif(pull != nil && pull.IsChecking(), 2000, 0)
ctx.Data["CanWriteToHeadRepo"] = canWriteToHeadRepo
ctx.Data["ShowMergeInstructions"] = canWriteToHeadRepo
data.ReloadingInterval = util.Iif(pull.IsChecking(), 2000, 0)
data.ShowMergeInstructions = canWriteToHeadRepo
data.ShowPullCommands = pull.HeadRepo != nil && !pull.HasMerged && !issue.IsClosed
ctx.Data["AllowMerge"] = allowMerge

prUnit, err := issue.Repo.GetUnit(ctx, unit.TypePullRequests)
if err != nil {
ctx.ServerError("GetUnit", err)
return
}
prConfig := prUnit.PullRequestsConfig()

ctx.Data["AutodetectManualMerge"] = prConfig.AutodetectManualMerge

var mergeStyle repo_model.MergeStyle
// Check correct values and select default
if ms, ok := ctx.Data["MergeStyle"].(repo_model.MergeStyle); !ok ||
!prConfig.IsMergeStyleAllowed(ms) {
if prConfig.IsMergeStyleAllowed(prConfig.DefaultMergeStyle) && !ok {
mergeStyle = prConfig.DefaultMergeStyle
} else if prConfig.AllowMerge {
mergeStyle = repo_model.MergeStyleMerge
} else if prConfig.AllowRebase {
mergeStyle = repo_model.MergeStyleRebase
} else if prConfig.AllowRebaseMerge {
mergeStyle = repo_model.MergeStyleRebaseMerge
} else if prConfig.AllowSquash {
mergeStyle = repo_model.MergeStyleSquash
} else if prConfig.AllowFastForwardOnly {
mergeStyle = repo_model.MergeStyleFastForwardOnly
} else if prConfig.AllowManualMerge {
mergeStyle = repo_model.MergeStyleManuallyMerged
}
}

ctx.Data["MergeStyle"] = mergeStyle

defaultMergeMessage, defaultMergeBody, err := pull_service.GetDefaultMergeMessage(ctx, ctx.Repo.GitRepo, pull, mergeStyle)
if err != nil {
ctx.ServerError("GetDefaultMergeMessage", err)
return
}
ctx.Data["DefaultMergeMessage"] = defaultMergeMessage
ctx.Data["DefaultMergeBody"] = defaultMergeBody

defaultSquashMergeMessage, defaultSquashMergeBody, err := pull_service.GetDefaultMergeMessage(ctx, ctx.Repo.GitRepo, pull, repo_model.MergeStyleSquash)
if err != nil {
ctx.ServerError("GetDefaultSquashMergeMessage", err)
return
}
ctx.Data["DefaultSquashMergeMessage"] = defaultSquashMergeMessage
ctx.Data["DefaultSquashMergeBody"] = defaultSquashMergeBody

pb := prInfo.ProtectedBranchRule
if pb != nil {
pb.Repo = pull.BaseRepo
Expand Down Expand Up @@ -995,6 +944,9 @@ func (prInfo *pullRequestViewInfo) prepareMergeBox(ctx *context.Context, issue *
return
}

prConfig := issue.Repo.MustGetUnit(ctx, unit.TypePullRequests).PullRequestsConfig()
data.AutodetectManualMerge = prConfig.AutodetectManualMerge

stillCanManualMerge := func() bool {
if pull.HasMerged || issue.IsClosed || !ctx.IsSigned {
return false
Expand All @@ -1007,13 +959,6 @@ func (prInfo *pullRequestViewInfo) prepareMergeBox(ctx *context.Context, issue *

ctx.Data["StillCanManualMerge"] = stillCanManualMerge()

// Check if there is a pending pr merge
ctx.Data["HasPendingPullRequestMerge"], ctx.Data["PendingPullRequestMerge"], err = pull_model.GetScheduledMergeByPullID(ctx, pull.ID)
if err != nil {
ctx.ServerError("GetScheduledMergeByPullID", err)
return
}

enableStatusCheck := pb != nil && pb.EnableStatusCheck
ctx.Data["EnableStatusCheck"] = enableStatusCheck

Expand Down Expand Up @@ -1043,6 +988,7 @@ func (prInfo *pullRequestViewInfo) prepareMergeBox(ctx *context.Context, issue *
(!data.requireSigned || data.willSign) // signing requirement is satisfied

ctx.Data["PullMergeBoxData"] = prInfo.MergeBoxData
prInfo.prepareMergeBoxFormProps(ctx)
}

func prepareIssueViewContent(ctx *context.Context, issue *issues_model.Issue) {
Expand Down
13 changes: 10 additions & 3 deletions routers/web/repo/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,12 +164,13 @@ func getPullInfo(ctx *context.Context) (issue *issues_model.Issue, ok bool) {
func (prInfo *pullRequestViewInfo) setTemplateDataMergeTarget(ctx *context.Context) {
pull := prInfo.issue.PullRequest
if ctx.Repo.Owner.Name == pull.MustHeadUserName(ctx) {
ctx.Data["HeadTarget"] = pull.HeadBranch
prInfo.headTarget = pull.HeadBranch
} else if pull.HeadRepo == nil {
ctx.Data["HeadTarget"] = ctx.Locale.Tr("repo.pull.deleted_branch", pull.HeadBranch)
prInfo.headTarget = ctx.Locale.TrString("repo.pull.deleted_branch", pull.HeadBranch)
} else {
ctx.Data["HeadTarget"] = pull.MustHeadUserName(ctx) + "/" + pull.HeadRepo.Name + ":" + pull.HeadBranch
prInfo.headTarget = pull.MustHeadUserName(ctx) + "/" + pull.HeadRepo.Name + ":" + pull.HeadBranch
}
ctx.Data["HeadTarget"] = prInfo.headTarget
ctx.Data["BaseTarget"] = pull.BaseBranch
headBranchLink := ""
if pull.Flow == issues_model.PullRequestFlowGithub {
Expand Down Expand Up @@ -268,6 +269,11 @@ type pullMergeBoxData struct {
HasOverridableBlockers bool
CanMergeNow bool

MergeFormProps map[string]any
ShowPullCommands bool
ShowMergeInstructions bool
AutodetectManualMerge bool

// don't expose unneeded fields to templates, need more refactoring changes
hasStatusCheckBlocker bool
isPullBranchDeletable bool
Expand All @@ -289,6 +295,7 @@ type pullRequestViewInfo struct {

IsPullRequestBroken bool
HeadBranchCommitID string
headTarget string // for display purpose only

CompareInfo git_service.CompareInfo
ProtectedBranchRule *git_model.ProtectedBranch
Expand Down
147 changes: 147 additions & 0 deletions routers/web/repo/pull_merge_form.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package repo

import (
"html/template"

pull_model "code.gitea.io/gitea/models/pull"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/services/context"
pull_service "code.gitea.io/gitea/services/pull"
)

func (prInfo *pullRequestViewInfo) prepareMergeBoxFormProps(ctx *context.Context) {
pull := prInfo.issue.PullRequest
prConfig := ctx.Repo.Repository.MustGetUnit(ctx, unit.TypePullRequests).PullRequestsConfig()

// Check correct values and select default
var mergeStyle repo_model.MergeStyle
if prConfig.IsMergeStyleAllowed(prConfig.DefaultMergeStyle) {
mergeStyle = prConfig.DefaultMergeStyle
} else if prConfig.AllowMerge {
mergeStyle = repo_model.MergeStyleMerge
} else if prConfig.AllowRebase {
mergeStyle = repo_model.MergeStyleRebase
} else if prConfig.AllowRebaseMerge {
mergeStyle = repo_model.MergeStyleRebaseMerge
} else if prConfig.AllowSquash {
mergeStyle = repo_model.MergeStyleSquash
} else if prConfig.AllowFastForwardOnly {
mergeStyle = repo_model.MergeStyleFastForwardOnly
} else if prConfig.AllowManualMerge {
mergeStyle = repo_model.MergeStyleManuallyMerged
}
if mergeStyle == "" {
Comment thread
wxiaoguang marked this conversation as resolved.
return
}

// Check if there is a pending pr merge
hasPendingPullRequestMerge, pendingPullRequestMerge, err := pull_model.GetScheduledMergeByPullID(ctx, pull.ID)
if err != nil {
ctx.ServerError("GetScheduledMergeByPullID", err)
return
}

var hasPendingPullRequestMergeTip template.HTML
if hasPendingPullRequestMerge {
createdPRMergeStr := templates.TimeSince(pendingPullRequestMerge.CreatedUnix)
hasPendingPullRequestMergeTip = ctx.Locale.Tr("repo.pulls.auto_merge_has_pending_schedule", pendingPullRequestMerge.Doer.Name, createdPRMergeStr)
}

defaultMergeTitle, defaultMergeBody, err := pull_service.GetDefaultMergeMessage(ctx, ctx.Repo.GitRepo, pull, mergeStyle)
if err != nil {
ctx.ServerError("GetDefaultMergeMessage", err)
return
}
defaultSquashMergeTitle, defaultSquashMergeBody, err := pull_service.GetDefaultMergeMessage(ctx, ctx.Repo.GitRepo, pull, repo_model.MergeStyleSquash)
if err != nil {
ctx.ServerError("GetDefaultSquashMergeMessage", err)
return
}

var defaultSquashMergeCommitMessages string
if !prInfo.IsPullRequestBroken {
defaultSquashMergeCommitMessages = pull_service.GetSquashMergeCommitMessages(ctx, pull)
}

allOverridableChecksOk := !prInfo.MergeBoxData.HasOverridableBlockers
prInfo.MergeBoxData.MergeFormProps = map[string]any{
"baseLink": prInfo.issue.Link(),
"textCancel": ctx.Locale.Tr("cancel"),
"textDeleteBranch": ctx.Locale.Tr("repo.branch.delete", prInfo.headTarget),
"textAutoMergeButtonWhenSucceed": ctx.Locale.Tr("repo.pulls.auto_merge_button_when_succeed"),
"textAutoMergeWhenSucceed": ctx.Locale.Tr("repo.pulls.auto_merge_when_succeed"),
"textAutoMergeCancelSchedule": ctx.Locale.Tr("repo.pulls.auto_merge_cancel_schedule"),
"textClearMergeMessage": ctx.Locale.Tr("repo.pulls.clear_merge_message"),
"textClearMergeMessageHint": ctx.Locale.Tr("repo.pulls.clear_merge_message_hint"),
"textMergeCommitId": ctx.Locale.Tr("repo.pulls.merge_commit_id"),

"canMergeNow": prInfo.MergeBoxData.CanMergeNow,
"allOverridableChecksOk": allOverridableChecksOk,
"emptyCommit": pull.IsEmpty(),
"pullHeadCommitID": prInfo.CompareInfo.HeadCommitID,
"isPullBranchDeletable": prInfo.MergeBoxData.isPullBranchDeletable,
"defaultMergeStyle": mergeStyle,
"defaultDeleteBranchAfterMerge": prConfig.DefaultDeleteBranchAfterMerge,
"mergeMessageFieldPlaceHolder": ctx.Locale.Tr("repo.editor.commit_message_desc"),
"defaultMergeMessage": defaultMergeBody,

"hasPendingPullRequestMerge": hasPendingPullRequestMerge,
"hasPendingPullRequestMergeTip": hasPendingPullRequestMergeTip,
}

// if this pr can be merged now, then hide the auto merge
generalHideAutoMerge := prInfo.MergeBoxData.CanMergeNow && allOverridableChecksOk

prInfo.MergeBoxData.MergeFormProps["mergeStyles"] = []any{
map[string]any{
"name": "merge",
"allowed": prConfig.AllowMerge,
"textDoMerge": ctx.Locale.Tr("repo.pulls.merge_pull_request"),
"mergeTitleFieldText": defaultMergeTitle,
"mergeMessageFieldText": defaultMergeBody,
"hideAutoMerge": generalHideAutoMerge,
},
map[string]any{
"name": "rebase",
"allowed": prConfig.AllowRebase,
"textDoMerge": ctx.Locale.Tr("repo.pulls.rebase_merge_pull_request"),
"hideMergeMessageTexts": true,
"hideAutoMerge": generalHideAutoMerge,
},
map[string]any{
"name": "rebase-merge",
"allowed": prConfig.AllowRebaseMerge,
"textDoMerge": ctx.Locale.Tr("repo.pulls.rebase_merge_commit_pull_request"),
"mergeTitleFieldText": defaultMergeTitle,
"mergeMessageFieldText": defaultMergeBody,
"hideAutoMerge": generalHideAutoMerge,
},
map[string]any{
"name": "squash",
"allowed": prConfig.AllowSquash,
"textDoMerge": ctx.Locale.Tr("repo.pulls.squash_merge_pull_request"),
"mergeTitleFieldText": defaultSquashMergeTitle,
"mergeMessageFieldText": defaultSquashMergeCommitMessages + defaultSquashMergeBody,
"hideAutoMerge": generalHideAutoMerge,
},
map[string]any{
"name": "fast-forward-only",
"allowed": prConfig.AllowFastForwardOnly && pull.CommitsBehind == 0,
"textDoMerge": ctx.Locale.Tr("repo.pulls.fast_forward_only_merge_pull_request"),
"hideMergeMessageTexts": true,
"hideAutoMerge": generalHideAutoMerge,
},
map[string]any{
"name": "manually-merged",
"allowed": prConfig.AllowManualMerge,
"textDoMerge": ctx.Locale.Tr("repo.pulls.merge_manually"),
"hideMergeMessageTexts": true,
"hideAutoMerge": true,
},
}
}
Loading