Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
639d23d
Fix duplicate script declaration error in pull request merge box
silverwind Feb 25, 2026
c04bf2e
Suppress djlint H025 false positive in pull merge box template
silverwind Feb 25, 2026
f7dcbd8
Read merge form data from element props instead of window.config.page…
silverwind Feb 26, 2026
6399e84
Merge remote-tracking branch 'origin/main' into fixmerge
silverwind Feb 27, 2026
71501fb
Move merge form JSON construction from template to Go
silverwind Feb 27, 2026
3cdf645
Apply suggestion from @silverwind
silverwind Feb 27, 2026
54dc981
Merge branch 'main' into fixmerge
silverwind Feb 27, 2026
83d9f38
Clean up merge form: pass params directly, remove unused code
silverwind Feb 27, 2026
365689a
Simplify merge form: remove params struct, read ctx.Data directly
silverwind Feb 27, 2026
66725dc
Reduce ctx.Data reads in merge form builder
silverwind Feb 27, 2026
db46564
Remove ctx.Data reads: pass values through typed params
silverwind Feb 27, 2026
4811c47
Remove obsolete ctx.Data sets and simplify merge style logic
silverwind Feb 27, 2026
6170a80
Remove ctx.Data round-trips in prepareViewPullInfo
silverwind Feb 27, 2026
1509ca3
DRY up merge form: embed struct, inline closure, deduplicate checks
silverwind Feb 27, 2026
1268eec
Pass pre-computed blocked-by flags to merge form builder
silverwind Feb 27, 2026
1898288
Add e2e tests for pull request merge box
silverwind Feb 27, 2026
ad0c12c
Optimize pull merge box e2e tests
silverwind Feb 27, 2026
b059455
Shorten e2e test names and use lowercase
silverwind Feb 27, 2026
33e154a
Remove redundant recovery check in status checks e2e test
silverwind Feb 27, 2026
96e8b2a
fix lint
silverwind Feb 27, 2026
93fae30
Fix type assertion panic when HeadTarget is template.HTML
silverwind Mar 1, 2026
4e72424
Remove unused apiCreateFile and extract preparePullViewReviewAndMergeAll
silverwind Mar 1, 2026
ff3e554
Remove unused clickDropdownItem utility function
silverwind Mar 1, 2026
885897b
Merge branch 'main' into fixmerge
silverwind Mar 1, 2026
a826a20
Add comments explaining merge permission logic
silverwind Mar 1, 2026
3e02cd4
Restore stillCanManualMerge closure
silverwind Mar 1, 2026
5ff665d
Rename merge form fields for clarity
silverwind Mar 6, 2026
f626aa0
Merge remote-tracking branch 'origin/main' into fixmerge
silverwind Mar 6, 2026
0affa9c
fmt
silverwind Mar 6, 2026
3d66fd3
Merge origin/main into fixmerge
silverwind Apr 2, 2026
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
91 changes: 60 additions & 31 deletions routers/web/repo/issue_view.go
Original file line number Diff line number Diff line change
Expand Up @@ -387,8 +387,6 @@ func ViewIssue(ctx *context.Context) {
prepareIssueViewSidebarTimeTracker,
prepareIssueViewSidebarDependency,
prepareIssueViewSidebarPin,
func(ctx *context.Context, issue *issues_model.Issue) { preparePullViewPullInfo(ctx, issue) },
preparePullViewReviewAndMerge,
}

for _, prepareFunc := range prepareFuncs {
Expand All @@ -398,6 +396,11 @@ func ViewIssue(ctx *context.Context) {
}
}

preparePullViewReviewAndMergeAll(ctx, issue)
if ctx.Written() {
return
}

// Get more information if it's a pull request.
if issue.IsPull {
if issue.PullRequest.HasMerged {
Expand Down Expand Up @@ -443,8 +446,7 @@ func ViewPullMergeBox(ctx *context.Context) {
ctx.NotFound(nil)
return
}
preparePullViewPullInfo(ctx, issue)
preparePullViewReviewAndMerge(ctx, issue)
preparePullViewReviewAndMergeAll(ctx, issue)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if ctx.Written() {
		return
	}

missed here?

ctx.Data["PullMergeBoxReloading"] = issue.PullRequest.IsChecking()

// TODO: it should use a dedicated struct to render the pull merge box, to make sure all data is prepared correctly
Expand Down Expand Up @@ -488,14 +490,15 @@ func prepareIssueViewSidebarDependency(ctx *context.Context, issue *issues_model
ctx.Data["BlockingDependencies"], ctx.Data["BlockingDependenciesNotPermitted"] = checkBlockedByIssues(ctx, blocking)
}

func preparePullViewSigning(ctx *context.Context, issue *issues_model.Issue) {
func preparePullViewSigning(ctx *context.Context, issue *issues_model.Issue) (willSign bool) {
if !issue.IsPull {
return
return false
}
pull := issue.PullRequest
ctx.Data["WillSign"] = false
if ctx.Doer != nil {
sign, key, _, err := asymkey_service.SignMerge(ctx, pull, ctx.Doer, ctx.Repo.GitRepo)
willSign = sign
ctx.Data["WillSign"] = sign
ctx.Data["SigningKeyMergeDisplay"] = asymkey_model.GetDisplaySigningKey(key)
if err != nil {
Expand All @@ -509,6 +512,7 @@ func preparePullViewSigning(ctx *context.Context, issue *issues_model.Issue) {
} else {
ctx.Data["WontSignReason"] = "not_signed_in"
}
return willSign
}

func prepareIssueViewSidebarWatch(ctx *context.Context, issue *issues_model.Issue) {
Expand Down Expand Up @@ -558,9 +562,9 @@ func prepareIssueViewSidebarTimeTracker(ctx *context.Context, issue *issues_mode
}
}

func preparePullViewDeleteBranch(ctx *context.Context, issue *issues_model.Issue, canDelete bool) {
func preparePullViewDeleteBranch(ctx *context.Context, issue *issues_model.Issue, canDelete bool) bool {
if !issue.IsPull {
return
return false
}
pull := issue.PullRequest
isPullBranchDeletable := canDelete &&
Expand All @@ -574,12 +578,13 @@ func preparePullViewDeleteBranch(ctx *context.Context, issue *issues_model.Issue
exist, err := issues_model.HasUnmergedPullRequestsByHeadInfo(ctx, pull.HeadRepoID, pull.HeadBranch)
if err != nil {
ctx.ServerError("HasUnmergedPullRequestsByHeadInfo", err)
return
return false
}

isPullBranchDeletable = !exist
}
ctx.Data["IsPullBranchDeletable"] = isPullBranchDeletable
return isPullBranchDeletable
}

func prepareIssueViewSidebarPin(ctx *context.Context, issue *issues_model.Issue) {
Expand Down Expand Up @@ -827,7 +832,15 @@ func prepareIssueViewCommentsAndSidebarParticipants(ctx *context.Context, issue
ctx.Data["NumParticipants"] = len(participants)
}

func preparePullViewReviewAndMerge(ctx *context.Context, issue *issues_model.Issue) {
func preparePullViewReviewAndMergeAll(ctx *context.Context, issue *issues_model.Issue) {
_, mergeInputs := preparePullViewPullInfo(ctx, issue)
if ctx.Written() {
return
}
preparePullViewReviewAndMerge(ctx, issue, mergeInputs)
}

func preparePullViewReviewAndMerge(ctx *context.Context, issue *issues_model.Issue, mergeInputs pullViewMergeInputs) {
getBranchData(ctx, issue)
if !issue.IsPull {
return
Expand Down Expand Up @@ -901,13 +914,9 @@ func preparePullViewReviewAndMerge(ctx *context.Context, issue *issues_model.Iss

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 := prConfig.DefaultMergeStyle
if !prConfig.IsMergeStyleAllowed(mergeStyle) {
if prConfig.AllowMerge {
mergeStyle = repo_model.MergeStyleMerge
} else if prConfig.AllowRebase {
mergeStyle = repo_model.MergeStyleRebase
Expand All @@ -922,51 +931,51 @@ func preparePullViewReviewAndMerge(ctx *context.Context, issue *issues_model.Iss
}
}

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, err := git_model.GetFirstMatchProtectedBranchRule(ctx, pull.BaseRepoID, pull.BaseBranch)
if err != nil {
ctx.ServerError("LoadProtectedBranch", err)
return
}

var isBlockedByApprovals, isBlockedByRejection, isBlockedByOfficialReviewRequests, isBlockedByOutdatedBranch, isBlockedByChangedProtectedFiles bool
if pb != nil {
pb.Repo = pull.BaseRepo
ctx.Data["ProtectedBranch"] = pb
ctx.Data["IsBlockedByApprovals"] = !issues_model.HasEnoughApprovals(ctx, pb, pull)
ctx.Data["IsBlockedByRejection"] = issues_model.MergeBlockedByRejectedReview(ctx, pb, pull)
ctx.Data["IsBlockedByOfficialReviewRequests"] = issues_model.MergeBlockedByOfficialReviewRequests(ctx, pb, pull)
ctx.Data["IsBlockedByOutdatedBranch"] = issues_model.MergeBlockedByOutdatedBranch(pb, pull)
isBlockedByApprovals = !issues_model.HasEnoughApprovals(ctx, pb, pull)
isBlockedByRejection = issues_model.MergeBlockedByRejectedReview(ctx, pb, pull)
isBlockedByOfficialReviewRequests = issues_model.MergeBlockedByOfficialReviewRequests(ctx, pb, pull)
isBlockedByOutdatedBranch = issues_model.MergeBlockedByOutdatedBranch(pb, pull)
isBlockedByChangedProtectedFiles = len(pull.ChangedProtectedFiles) != 0
ctx.Data["IsBlockedByApprovals"] = isBlockedByApprovals
ctx.Data["IsBlockedByRejection"] = isBlockedByRejection
ctx.Data["IsBlockedByOfficialReviewRequests"] = isBlockedByOfficialReviewRequests
ctx.Data["IsBlockedByOutdatedBranch"] = isBlockedByOutdatedBranch
ctx.Data["IsBlockedByChangedProtectedFiles"] = isBlockedByChangedProtectedFiles
ctx.Data["GrantedApprovals"] = issues_model.GetGrantedApprovalsCount(ctx, pb, pull)
ctx.Data["RequireSigned"] = pb.RequireSignedCommits
ctx.Data["ChangedProtectedFiles"] = pull.ChangedProtectedFiles
ctx.Data["IsBlockedByChangedProtectedFiles"] = len(pull.ChangedProtectedFiles) != 0
ctx.Data["ChangedProtectedFilesNum"] = len(pull.ChangedProtectedFiles)
ctx.Data["RequireApprovalsWhitelist"] = pb.EnableApprovalsWhitelist
}

preparePullViewSigning(ctx, issue)
willSign := preparePullViewSigning(ctx, issue)
if ctx.Written() {
return
}

preparePullViewDeleteBranch(ctx, issue, canDelete)
isPullBranchDeletable := preparePullViewDeleteBranch(ctx, issue, canDelete)
if ctx.Written() {
return
}
Expand All @@ -988,11 +997,31 @@ func preparePullViewReviewAndMerge(ctx *context.Context, issue *issues_model.Iss
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)
_, pendingPullRequestMerge, err := pull_model.GetScheduledMergeByPullID(ctx, pull.ID)
if err != nil {
ctx.ServerError("GetScheduledMergeByPullID", err)
return
}

preparePullViewMergeFormData(ctx, issue, &mergeFormParams{
pullViewMergeInputs: mergeInputs,
AllowMerge: allowMerge,
ProtectedBranch: pb,
PrConfig: prConfig,
MergeStyle: mergeStyle,
DefaultMergeMessage: defaultMergeMessage,
DefaultMergeBody: defaultMergeBody,
DefaultSquashMergeMessage: defaultSquashMergeMessage,
DefaultSquashMergeBody: defaultSquashMergeBody,
PendingPullRequestMerge: pendingPullRequestMerge,
IsBlockedByApprovals: isBlockedByApprovals,
IsBlockedByRejection: isBlockedByRejection,
IsBlockedByOfficialReviewRequests: isBlockedByOfficialReviewRequests,
IsBlockedByOutdatedBranch: isBlockedByOutdatedBranch,
IsBlockedByChangedProtectedFiles: isBlockedByChangedProtectedFiles,
WillSign: willSign,
IsPullBranchDeletable: isPullBranchDeletable,
})
}

func prepareIssueViewContent(ctx *context.Context, issue *issues_model.Issue) {
Expand Down
Loading
Loading