diff --git a/routers/web/repo/issue_view.go b/routers/web/repo/issue_view.go index af13a1156ed..852b880ab05 100644 --- a/routers/web/repo/issue_view.go +++ b/routers/web/repo/issue_view.go @@ -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" @@ -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 @@ -834,14 +834,12 @@ 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) @@ -849,7 +847,6 @@ func (prInfo *pullRequestViewInfo) prepareMergeBox(ctx *context.Context, issue * ctx.ServerError("IsUserAllowedToUpdate", err) return } - ctx.Data["GetCommitMessages"] = pull_service.GetSquashMergeCommitMessages(ctx, pull) } if pull.IsFilesConflicted() { @@ -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 @@ -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 @@ -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 @@ -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) { diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index c532cbba22d..7b31a26d6f1 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -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 { @@ -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 @@ -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 diff --git a/routers/web/repo/pull_merge_form.go b/routers/web/repo/pull_merge_form.go new file mode 100644 index 00000000000..b390fd69349 --- /dev/null +++ b/routers/web/repo/pull_merge_form.go @@ -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 == "" { + 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, + }, + } +} diff --git a/templates/repo/issue/view_content/pull_merge_box.tmpl b/templates/repo/issue/view_content/pull_merge_box.tmpl index 84d703debc5..768d3b31194 100644 --- a/templates/repo/issue/view_content/pull_merge_box.tmpl +++ b/templates/repo/issue/view_content/pull_merge_box.tmpl @@ -208,100 +208,11 @@ {{end}} {{if .AllowMerge}} {{/* user is allowed to merge */}} - {{$prUnit := .Repository.MustGetUnit ctx ctx.Consts.RepoUnitTypePullRequests}} - {{if or $prUnit.PullRequestsConfig.AllowMerge $prUnit.PullRequestsConfig.AllowRebase $prUnit.PullRequestsConfig.AllowRebaseMerge $prUnit.PullRequestsConfig.AllowSquash $prUnit.PullRequestsConfig.AllowFastForwardOnly}} - {{$hasPendingPullRequestMergeTip := ""}} - {{if .HasPendingPullRequestMerge}} - {{$createdPRMergeStr := DateUtils.TimeSince .PendingPullRequestMerge.CreatedUnix}} - {{$hasPendingPullRequestMergeTip = ctx.Locale.Tr "repo.pulls.auto_merge_has_pending_schedule" .PendingPullRequestMerge.Doer.Name $createdPRMergeStr}} - {{end}} + {{if $data.MergeFormProps}}
- - {{$showGeneralMergeForm = true}} {{/* The merge form is a Vue component. After mounted, it has a button for choosing merge style, so make it have min-height to avoid layout shifting */}} - + {{else}} {{/* no merge style was set in repo setting: not or ($prUnit.PullRequestsConfig.AllowMerge ...) */}} @@ -396,8 +307,8 @@ {{end}} - {{if and .Issue.PullRequest.HeadRepo (not .Issue.PullRequest.HasMerged) (not .Issue.IsClosed)}} - {{template "repo/issue/view_content/pull_merge_instruction" dict "PullRequest" .Issue.PullRequest "ShowMergeInstructions" .ShowMergeInstructions}} + {{if $data.ShowPullCommands}} + {{template "repo/issue/view_content/pull_merge_instruction" dict "PullRequest" .Issue.PullRequest "MergeBoxData" $data}} {{end}} diff --git a/templates/repo/issue/view_content/pull_merge_instruction.tmpl b/templates/repo/issue/view_content/pull_merge_instruction.tmpl index b52333466d8..ad85c004505 100644 --- a/templates/repo/issue/view_content/pull_merge_instruction.tmpl +++ b/templates/repo/issue/view_content/pull_merge_instruction.tmpl @@ -1,57 +1,59 @@ +{{$data := $.MergeBoxData}} +{{$pull := $.PullRequest}}