Refactor pull request view (6) (#37522)

Clean up legacy logic.

* Use backend logic to choose PR timeline icon color
* Always use the Vue form to merge, remove the "StillCanManualMerge" logic
This commit is contained in:
wxiaoguang
2026-05-04 14:15:33 +08:00
committed by GitHub
parent f26f71f1b2
commit dd17521808
16 changed files with 293 additions and 316 deletions
+5 -16
View File
@@ -835,14 +835,14 @@ func (prInfo *pullRequestViewInfo) prepareMergeBox(ctx *context.Context, issue *
}
canDelete := false
allowMerge := false
canWriteToHeadRepo := false
pull_service.StartPullRequestCheckOnView(ctx, pull)
if !prInfo.IsPullRequestBroken {
data.ShowUpdatePullInfo = pull.CommitsBehind > 0 && !issue.IsClosed && !pull.IsChecking() && !pull.IsFilesConflicted() && !prInfo.IsPullRequestBroken
var err error
ctx.Data["UpdateAllowed"], ctx.Data["UpdateByRebaseAllowed"], err = pull_service.IsUserAllowedToUpdate(ctx, pull, ctx.Doer)
data.UpdateAllowed, data.UpdateByRebaseAllowed, err = pull_service.IsUserAllowedToUpdate(ctx, pull, ctx.Doer)
if err != nil {
ctx.ServerError("IsUserAllowedToUpdate", err)
return
@@ -888,7 +888,7 @@ func (prInfo *pullRequestViewInfo) prepareMergeBox(ctx *context.Context, issue *
if !canWriteToHeadRepo { // maintainers maybe allowed to push to head repo even if they can't write to it
canWriteToHeadRepo = pull.AllowMaintainerEdit && perm.CanWrite(unit.TypeCode)
}
allowMerge, err = pull_service.IsUserAllowedToMerge(ctx, pull, perm, ctx.Doer)
data.allowMerge, err = pull_service.IsUserAllowedToMerge(ctx, pull, perm, ctx.Doer)
if err != nil {
ctx.ServerError("IsUserAllowedToMerge", err)
return
@@ -903,7 +903,7 @@ func (prInfo *pullRequestViewInfo) prepareMergeBox(ctx *context.Context, issue *
data.ReloadingInterval = util.Iif(pull.IsChecking(), 2000, 0)
data.ShowMergeInstructions = canWriteToHeadRepo
data.ShowPullCommands = pull.HeadRepo != nil && !pull.HasMerged && !issue.IsClosed
ctx.Data["AllowMerge"] = allowMerge
ctx.Data["AllowMerge"] = data.allowMerge
pb := prInfo.ProtectedBranchRule
if pb != nil {
@@ -947,18 +947,6 @@ func (prInfo *pullRequestViewInfo) prepareMergeBox(ctx *context.Context, issue *
prConfig := issue.Repo.MustGetUnit(ctx, unit.TypePullRequests).PullRequestsConfig()
data.AutodetectManualMerge = prConfig.AutodetectManualMerge
stillCanManualMerge := func() bool {
if pull.HasMerged || issue.IsClosed || !ctx.IsSigned {
return false
}
if pull.IsStatusMergeable() || pull.IsWorkInProgress(ctx) || pull.IsChecking() {
return false
}
return allowMerge && prConfig.AllowManualMerge
}
ctx.Data["StillCanManualMerge"] = stillCanManualMerge()
enableStatusCheck := pb != nil && pb.EnableStatusCheck
ctx.Data["EnableStatusCheck"] = enableStatusCheck
@@ -989,6 +977,7 @@ func (prInfo *pullRequestViewInfo) prepareMergeBox(ctx *context.Context, issue *
ctx.Data["PullMergeBoxData"] = prInfo.MergeBoxData
prInfo.prepareMergeBoxFormProps(ctx)
prInfo.prepareMergeBoxIconColor()
}
func prepareIssueViewContent(ctx *context.Context, issue *issues_model.Issue) {
+16 -5
View File
@@ -266,8 +266,15 @@ type pullMergeBoxData struct {
ShowMergeBox bool
ReloadingInterval int
TimelineIconClass string
HasOverridableBlockers bool
CanMergeNow bool
CanMergeNow bool // PR is mergeable, either no blocker, or doer is admin and can bypass the blockers
allowMerge bool // doer has permission to merge
ShowUpdatePullInfo bool
UpdateAllowed bool
UpdateByRebaseAllowed bool
MergeFormProps map[string]any
ShowPullCommands bool
@@ -302,6 +309,9 @@ type pullRequestViewInfo struct {
StatusCheckData *pullCommitStatusCheckData
CommitStatuses []*git_model.CommitStatus
MergeBoxData *pullMergeBoxData
enableStatusCheck bool
workInProgressPrefix string
}
func newPullRequestViewInfo() *pullRequestViewInfo {
@@ -430,8 +440,8 @@ func (prInfo *pullRequestViewInfo) prepareViewFillCommitStatusInfoForOpen(ctx *c
}
pb := prInfo.ProtectedBranchRule
enableStatusCheck := pb != nil && pb.EnableStatusCheck
if !enableStatusCheck {
prInfo.enableStatusCheck = pb != nil && pb.EnableStatusCheck
if !prInfo.enableStatusCheck {
return
}
@@ -549,9 +559,10 @@ func (prInfo *pullRequestViewInfo) prepareViewOpenPullInfo(ctx *context.Context)
}
// this one is used by both sidebar and merge-box
prInfo.workInProgressPrefix = pull.GetWorkInProgressPrefix(ctx)
if pull.IsWorkInProgress(ctx) {
ctx.Data["IsPullWorkInProgress"] = true
ctx.Data["WorkInProgressPrefix"] = pull.GetWorkInProgressPrefix(ctx)
ctx.Data["IsPullWorkInProgress"] = prInfo.workInProgressPrefix != ""
ctx.Data["WorkInProgressPrefix"] = prInfo.workInProgressPrefix
}
}
+33
View File
@@ -0,0 +1,33 @@
// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package repo
func (prInfo *pullRequestViewInfo) prepareMergeBoxIconColor() {
pull := prInfo.issue.PullRequest
mergeBoxData := prInfo.MergeBoxData
statusCheckData := prInfo.StatusCheckData
switch {
case pull.HasMerged:
prInfo.MergeBoxData.TimelineIconClass = "tw-text-purple"
case prInfo.issue.IsClosed, prInfo.workInProgressPrefix != "", pull.IsFilesConflicted():
prInfo.MergeBoxData.TimelineIconClass = "tw-text-text-light"
case prInfo.IsPullRequestBroken, mergeBoxData.isBlockedByApprovals, mergeBoxData.isBlockedByRejection,
mergeBoxData.isBlockedByOfficialReviewRequests, mergeBoxData.isBlockedByOutdatedBranch, mergeBoxData.isBlockedByChangedProtectedFiles:
prInfo.MergeBoxData.TimelineIconClass = "tw-text-red"
case prInfo.enableStatusCheck && (statusCheckData.RequiredChecksState.IsFailure() || statusCheckData.RequiredChecksState.IsError()):
prInfo.MergeBoxData.TimelineIconClass = "tw-text-red"
case prInfo.enableStatusCheck && (statusCheckData.LatestCommitStatus == nil || statusCheckData.RequiredChecksState.IsPending() || statusCheckData.RequiredChecksState.IsWarning()):
prInfo.MergeBoxData.TimelineIconClass = "tw-text-yellow"
case mergeBoxData.allowMerge && mergeBoxData.requireSigned && !mergeBoxData.willSign:
prInfo.MergeBoxData.TimelineIconClass = "tw-text-red"
case pull.IsChecking():
prInfo.MergeBoxData.TimelineIconClass = "tw-text-yellow"
case pull.IsEmpty():
prInfo.MergeBoxData.TimelineIconClass = "tw-text-text-light"
case pull.IsStatusMergeable():
prInfo.MergeBoxData.TimelineIconClass = "tw-text-green"
default:
prInfo.MergeBoxData.TimelineIconClass = "tw-text-red"
}
}
+73 -42
View File
@@ -16,6 +16,13 @@ import (
func (prInfo *pullRequestViewInfo) prepareMergeBoxFormProps(ctx *context.Context) {
pull := prInfo.issue.PullRequest
if pull.HasMerged || prInfo.issue.IsClosed {
return
}
if !prInfo.MergeBoxData.allowMerge {
return
}
prConfig := ctx.Repo.Repository.MustGetUnit(ctx, unit.TypePullRequests).PullRequestsConfig()
// Check correct values and select default
@@ -69,7 +76,7 @@ func (prInfo *pullRequestViewInfo) prepareMergeBoxFormProps(ctx *context.Context
}
allOverridableChecksOk := !prInfo.MergeBoxData.HasOverridableBlockers
prInfo.MergeBoxData.MergeFormProps = map[string]any{
mergeFormProps := map[string]any{
"baseLink": prInfo.issue.Link(),
"textCancel": ctx.Locale.Tr("cancel"),
"textDeleteBranch": ctx.Locale.Tr("repo.branch.delete", prInfo.headTarget),
@@ -97,51 +104,75 @@ func (prInfo *pullRequestViewInfo) prepareMergeBoxFormProps(ctx *context.Context
// 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{
var mergeStyles []any
if pull.IsStatusMergeable() {
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,
},
}
}
canUseManualMerge := func() bool {
if pull.IsWorkInProgress(ctx) || pull.IsChecking() {
return false
}
return prConfig.AllowManualMerge
}
// Manually Merged is not a well-known feature, it is used to mark a non-mergeable PR (already merged, conflicted) as merged
// To test it:
// Enable "Manually Merged" feature in the Repository Settings
// Create a pull request, either:
// - Merge the pull request branch locally and push the merged commit to Gitea
// - Make some conflicts between the base branch and the pull request branch
// Then the Manually Merged form will be shown in the merge form
if canUseManualMerge() {
mergeStyles = append(mergeStyles, map[string]any{
"name": "manually-merged",
"allowed": prConfig.AllowManualMerge,
"textDoMerge": ctx.Locale.Tr("repo.pulls.merge_manually"),
"hideMergeMessageTexts": true,
"hideAutoMerge": true,
},
})
}
if len(mergeStyles) > 0 {
mergeFormProps["mergeStyles"] = mergeStyles
prInfo.MergeBoxData.MergeFormProps = mergeFormProps
}
}