mirror of
https://github.com/go-gitea/gitea.git
synced 2026-09-16 01:43:24 +09:00
feat: Add default PR branch update style setting (#37410)
Adds repository-level settings for pull request branch updates so admins can choose the default update method and disable merge or rebase updates. <img width="1025" height="158" src="https://github.com/user-attachments/assets/d030973b-0ddd-4035-b04f-145c445084d7" /> --------- Co-authored-by: OpenAI Codex (GPT-5) <codex@openai.com> Co-authored-by: silverwind <me@silverwind.io> Co-authored-by: Claude (Opus 4.7) <noreply@anthropic.com> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
co-authored by
OpenAI Codex
silverwind
Claude
wxiaoguang
parent
16189a68c4
commit
34fd3c9f06
@@ -867,10 +867,8 @@ func (prInfo *pullRequestViewInfo) prepareMergeBox(ctx *context.Context, issue *
|
||||
|
||||
if !prInfo.IsPullRequestBroken {
|
||||
data.ShowUpdatePullInfo = pull.CommitsBehind > 0 && !issue.IsClosed && !pull.IsChecking() && !pull.IsFilesConflicted() && !prInfo.IsPullRequestBroken
|
||||
var err error
|
||||
data.UpdateAllowed, data.UpdateByRebaseAllowed, err = pull_service.IsUserAllowedToUpdate(ctx, pull, ctx.Doer)
|
||||
if err != nil {
|
||||
ctx.ServerError("IsUserAllowedToUpdate", err)
|
||||
prInfo.preparePullUpdateActions(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -975,6 +973,45 @@ func (prInfo *pullRequestViewInfo) prepareMergeBox(ctx *context.Context, issue *
|
||||
ctx.Data["PullMergeBoxData"] = prInfo.MergeBoxData
|
||||
}
|
||||
|
||||
func (prInfo *pullRequestViewInfo) preparePullUpdateActions(ctx *context.Context) {
|
||||
pull := prInfo.issue.PullRequest
|
||||
data := prInfo.MergeBoxData
|
||||
userUpdateStyles, err := pull_service.CheckUserAllowedToUpdate(ctx, pull, ctx.Doer)
|
||||
if err != nil {
|
||||
ctx.ServerError("IsUserAllowedToUpdate", err)
|
||||
return
|
||||
}
|
||||
if !userUpdateStyles.MergeAllowed && !userUpdateStyles.RebaseAllowed {
|
||||
return
|
||||
}
|
||||
|
||||
issueLink := prInfo.issue.Link()
|
||||
mergeAction := &pullUpdateAction{
|
||||
URL: issueLink + "/update?style=merge",
|
||||
Text: ctx.Tr("repo.pulls.update_branch"),
|
||||
}
|
||||
rebaseAction := &pullUpdateAction{
|
||||
URL: issueLink + "/update?style=rebase",
|
||||
Text: ctx.Tr("repo.pulls.update_branch_rebase"),
|
||||
}
|
||||
|
||||
if userUpdateStyles.MergeAllowed {
|
||||
data.UpdateStyleOptions = append(data.UpdateStyleOptions, mergeAction)
|
||||
}
|
||||
if userUpdateStyles.RebaseAllowed {
|
||||
data.UpdateStyleOptions = append(data.UpdateStyleOptions, rebaseAction)
|
||||
}
|
||||
|
||||
if userUpdateStyles.DefaultUpdateStyle == repo_model.UpdateStyleRebase && userUpdateStyles.RebaseAllowed {
|
||||
data.UpdatePrimaryAction = rebaseAction
|
||||
} else if userUpdateStyles.DefaultUpdateStyle == repo_model.UpdateStyleMerge && userUpdateStyles.MergeAllowed {
|
||||
data.UpdatePrimaryAction = mergeAction
|
||||
} else {
|
||||
data.UpdatePrimaryAction = data.UpdateStyleOptions[0]
|
||||
}
|
||||
data.UpdatePrimaryAction.Selected = true
|
||||
}
|
||||
|
||||
func (prInfo *pullRequestViewInfo) prepareMergeBoxProtectionChecks(ctx *context.Context) {
|
||||
pb, err := git_model.GetFirstMatchProtectedBranchRule(ctx, ctx.Repo.Repository.ID, prInfo.issue.PullRequest.BaseBranch)
|
||||
if err != nil {
|
||||
|
||||
@@ -280,9 +280,9 @@ type pullMergeBoxData struct {
|
||||
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
|
||||
ShowUpdatePullInfo bool
|
||||
UpdatePrimaryAction *pullUpdateAction
|
||||
UpdateStyleOptions []*pullUpdateAction
|
||||
|
||||
MergeFormProps map[string]any
|
||||
ShowPullCommands bool
|
||||
@@ -308,6 +308,12 @@ type pullMergeBoxData struct {
|
||||
InfoSections []*pullInfoSection
|
||||
}
|
||||
|
||||
type pullUpdateAction struct {
|
||||
URL string
|
||||
Text template.HTML
|
||||
Selected bool
|
||||
}
|
||||
|
||||
// pullRequestViewInfo is a structured type for viewing pull request
|
||||
// Refactoring plan:
|
||||
// * move dynamic template-data-based variable into this struct
|
||||
@@ -957,8 +963,6 @@ func UpdatePullRequest(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
rebase := ctx.FormString("style") == "rebase"
|
||||
|
||||
if err := issue.PullRequest.LoadBaseRepo(ctx); err != nil {
|
||||
ctx.ServerError("LoadBaseRepo", err)
|
||||
return
|
||||
@@ -968,13 +972,14 @@ func UpdatePullRequest(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
allowedUpdateByMerge, allowedUpdateByRebase, err := pull_service.IsUserAllowedToUpdate(ctx, issue.PullRequest, ctx.Doer)
|
||||
userUpdateStyles, err := pull_service.CheckUserAllowedToUpdate(ctx, issue.PullRequest, ctx.Doer)
|
||||
if err != nil {
|
||||
ctx.ServerError("IsUserAllowedToMerge", err)
|
||||
return
|
||||
}
|
||||
|
||||
if (!allowedUpdateByMerge && !rebase) || (rebase && !allowedUpdateByRebase) {
|
||||
rebase := ctx.FormString("style", string(userUpdateStyles.DefaultUpdateStyle)) == string(repo_model.UpdateStyleRebase)
|
||||
if (rebase && !userUpdateStyles.RebaseAllowed) || (!rebase && !userUpdateStyles.MergeAllowed) {
|
||||
ctx.JSONError(ctx.Tr("repo.pulls.update_not_allowed"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ package setting
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -49,6 +50,12 @@ const (
|
||||
tplDeployKeys templates.TplName = "repo/settings/deploy_keys"
|
||||
)
|
||||
|
||||
type selectOption struct {
|
||||
Value string
|
||||
Text template.HTML
|
||||
Selected bool
|
||||
}
|
||||
|
||||
// SettingsCtxData is a middleware that sets all the general context data for the
|
||||
// settings template.
|
||||
func SettingsCtxData(ctx *context.Context) {
|
||||
@@ -66,6 +73,7 @@ func SettingsCtxData(ctx *context.Context) {
|
||||
ctx.Data["SigningKeyAvailable"] = signing != nil
|
||||
ctx.Data["SigningSettings"] = setting.Repository.Signing
|
||||
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
|
||||
preparePullRequestSettings(ctx)
|
||||
|
||||
if ctx.Doer.IsAdmin {
|
||||
if setting.Indexer.RepoIndexerEnabled {
|
||||
@@ -96,6 +104,35 @@ func SettingsCtxData(ctx *context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
func preparePullRequestSettings(ctx *context.Context) {
|
||||
defaultUpdateStyle := repo_model.UpdateStyleMerge
|
||||
if ctx.Repo.Repository.UnitEnabled(ctx, unit_model.TypePullRequests) {
|
||||
prUnit := ctx.Repo.Repository.MustGetUnit(ctx, unit_model.TypePullRequests)
|
||||
defaultUpdateStyle = util.IfZero(prUnit.PullRequestsConfig().DefaultUpdateStyle, repo_model.UpdateStyleMerge)
|
||||
}
|
||||
|
||||
updateBranchText := ctx.Tr("repo.pulls.update_branch")
|
||||
rebaseUpdateText := ctx.Tr("repo.pulls.update_branch_rebase")
|
||||
defaultUpdateStyleText := updateBranchText
|
||||
if defaultUpdateStyle == repo_model.UpdateStyleRebase {
|
||||
defaultUpdateStyleText = rebaseUpdateText
|
||||
}
|
||||
|
||||
ctx.Data["PullsDefaultUpdateStyleText"] = defaultUpdateStyleText
|
||||
ctx.Data["PullsDefaultUpdateStyleOptions"] = []selectOption{
|
||||
{
|
||||
Value: string(repo_model.UpdateStyleMerge),
|
||||
Text: updateBranchText,
|
||||
Selected: defaultUpdateStyle == repo_model.UpdateStyleMerge,
|
||||
},
|
||||
{
|
||||
Value: string(repo_model.UpdateStyleRebase),
|
||||
Text: rebaseUpdateText,
|
||||
Selected: defaultUpdateStyle == repo_model.UpdateStyleRebase,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Settings show a repository's settings page
|
||||
func Settings(ctx *context.Context) {
|
||||
ctx.HTML(http.StatusOK, tplSettingsOptions)
|
||||
@@ -616,7 +653,8 @@ func handleSettingsPostAdvanced(ctx *context.Context) {
|
||||
}
|
||||
|
||||
if form.EnablePulls && !unit_model.TypePullRequests.UnitGlobalDisabled() {
|
||||
units = append(units, newRepoUnit(repo, unit_model.TypePullRequests, &repo_model.PullRequestsConfig{
|
||||
defaultUpdateStyle := util.IfZero(repo_model.UpdateStyle(form.PullsDefaultUpdateStyle), repo_model.UpdateStyleMerge)
|
||||
prConfig := &repo_model.PullRequestsConfig{
|
||||
IgnoreWhitespaceConflicts: form.PullsIgnoreWhitespace,
|
||||
AllowMerge: form.PullsAllowMerge,
|
||||
AllowRebase: form.PullsAllowRebase,
|
||||
@@ -625,12 +663,20 @@ func handleSettingsPostAdvanced(ctx *context.Context) {
|
||||
AllowFastForwardOnly: form.PullsAllowFastForwardOnly,
|
||||
AllowManualMerge: form.PullsAllowManualMerge,
|
||||
AutodetectManualMerge: form.EnableAutodetectManualMerge,
|
||||
AllowMergeUpdate: form.PullsAllowMergeUpdate,
|
||||
AllowRebaseUpdate: form.PullsAllowRebaseUpdate,
|
||||
DefaultUpdateStyle: defaultUpdateStyle,
|
||||
DefaultDeleteBranchAfterMerge: form.DefaultDeleteBranchAfterMerge,
|
||||
DefaultMergeStyle: repo_model.MergeStyle(form.PullsDefaultMergeStyle),
|
||||
DefaultAllowMaintainerEdit: form.DefaultAllowMaintainerEdit,
|
||||
DefaultTargetBranch: strings.TrimSpace(form.DefaultTargetBranch),
|
||||
}))
|
||||
}
|
||||
if err := prConfig.ValidateUpdateSettings(); err != nil {
|
||||
ctx.Flash.Error(err.Error())
|
||||
ctx.Redirect(repo.Link() + "/settings")
|
||||
return
|
||||
}
|
||||
units = append(units, newRepoUnit(repo, unit_model.TypePullRequests, prConfig))
|
||||
} else if !unit_model.TypePullRequests.UnitGlobalDisabled() {
|
||||
deleteUnitTypes = append(deleteUnitTypes, unit_model.TypePullRequests)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user