Allow configuring default PR base branch (fixes #36412) (#36425)

This adds a per-repository default PR base branch and wires it through
PR entry points. It updates compare links and recently pushed branch
prompts to respect the configured base branch, and prevents auto-merge
cleanup from deleting the configured base branch on same-repo PRs.

## Behavior changes
- New PR compare links on repo home/issue list and branch list honor the
configured default PR base branch.
- The "recently pushed new branches" prompt now compares against the
configured base branch.
- Auto-merge branch cleanup skips deleting the configured base branch
(same-repo PRs only).

---------

Signed-off-by: Louis <116039387+tototomate123@users.noreply.github.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
This commit is contained in:
Louis
2026-02-07 02:34:29 +01:00
committed by GitHub
parent 0dacd956fb
commit e2104a1dd5
25 changed files with 213 additions and 99 deletions
+1 -1
View File
@@ -155,7 +155,7 @@ func DeleteBranch(ctx *context.APIContext) {
case git.IsErrBranchNotExist(err):
ctx.APIErrorNotFound(err)
case errors.Is(err, repo_service.ErrBranchIsDefault):
ctx.APIError(http.StatusForbidden, errors.New("can not delete default branch"))
ctx.APIError(http.StatusForbidden, errors.New("can not delete default or pull request target branch"))
case errors.Is(err, git_model.ErrBranchIsProtected):
ctx.APIError(http.StatusForbidden, errors.New("branch protected"))
default:
+1 -1
View File
@@ -1138,7 +1138,7 @@ func parseCompareInfo(ctx *context.APIContext, compareParam string) (result *git
return nil, nil
}
baseRef := ctx.Repo.GitRepo.UnstableGuessRefByShortName(util.IfZero(compareReq.BaseOriRef, baseRepo.DefaultBranch))
baseRef := ctx.Repo.GitRepo.UnstableGuessRefByShortName(util.IfZero(compareReq.BaseOriRef, baseRepo.GetPullRequestTargetBranch(ctx)))
headRef := headGitRepo.UnstableGuessRefByShortName(util.IfZero(compareReq.HeadOriRef, headRepo.DefaultBranch))
log.Trace("Repo path: %q, base ref: %q->%q, head ref: %q->%q", ctx.Repo.Repository.RelativePath(), compareReq.BaseOriRef, baseRef, compareReq.HeadOriRef, headRef)
+1
View File
@@ -41,6 +41,7 @@ func Branches(ctx *context.Context) {
ctx.Data["AllowsPulls"] = ctx.Repo.Repository.AllowsPulls(ctx)
ctx.Data["IsWriter"] = ctx.Repo.CanWrite(unit.TypeCode)
ctx.Data["IsMirror"] = ctx.Repo.Repository.IsMirror
// TODO: Can be replaced by ctx.Repo.PullRequestCtx.CanCreateNewPull()
ctx.Data["CanPull"] = ctx.Repo.CanWrite(unit.TypeCode) ||
(ctx.IsSigned && repo_model.HasForkedRepo(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID))
ctx.Data["PageIsViewCode"] = true
+3 -3
View File
@@ -247,7 +247,7 @@ func ParseCompareInfo(ctx *context.Context) *git_service.CompareInfo {
}
// 4 get base and head refs
baseRefName := util.IfZero(compareReq.BaseOriRef, baseRepo.DefaultBranch)
baseRefName := util.IfZero(compareReq.BaseOriRef, baseRepo.GetPullRequestTargetBranch(ctx))
headRefName := util.IfZero(compareReq.HeadOriRef, headRepo.DefaultBranch)
baseRef := ctx.Repo.GitRepo.UnstableGuessRefByShortName(baseRefName)
@@ -276,10 +276,10 @@ func ParseCompareInfo(ctx *context.Context) *git_service.CompareInfo {
ctx.Data["BaseBranch"] = baseRef.ShortName() // for legacy templates
ctx.Data["HeadUser"] = headOwner
ctx.Data["HeadBranch"] = headRef.ShortName() // for legacy templates
ctx.Repo.PullRequest.SameRepo = isSameRepo
ctx.Data["IsPull"] = true
context.InitRepoPullRequestCtx(ctx, baseRepo, headRepo)
// The current base and head repositories and branches may not
// actually be the intended branches that the user wants to
// create a pull-request from - but also determining the head
-5
View File
@@ -109,11 +109,6 @@ func MustAllowPulls(ctx *context.Context) {
ctx.NotFound(nil)
return
}
// User can send pull request if owns a forked repository.
if ctx.IsSigned && repo_model.HasForkedRepo(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID) {
ctx.Repo.PullRequest.Allowed = true
}
}
func retrieveProjectsInternal(ctx *context.Context, repo *repo_model.Repository) (open, closed []*project_model.Project) {
+7
View File
@@ -28,6 +28,7 @@ import (
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/validation"
"code.gitea.io/gitea/modules/web"
repo_router "code.gitea.io/gitea/routers/web/repo"
actions_service "code.gitea.io/gitea/services/actions"
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
@@ -88,6 +89,11 @@ func SettingsCtxData(ctx *context.Context) {
return
}
ctx.Data["PushMirrors"] = pushMirrors
repo_router.PrepareBranchList(ctx)
if ctx.Written() {
return
}
}
// Settings show a repository's settings page
@@ -622,6 +628,7 @@ func handleSettingsPostAdvanced(ctx *context.Context) {
DefaultDeleteBranchAfterMerge: form.DefaultDeleteBranchAfterMerge,
DefaultMergeStyle: repo_model.MergeStyle(form.PullsDefaultMergeStyle),
DefaultAllowMaintainerEdit: form.DefaultAllowMaintainerEdit,
DefaultTargetBranch: strings.TrimSpace(form.DefaultTargetBranch),
}))
} else if !unit_model.TypePullRequests.UnitGlobalDisabled() {
deleteUnitTypes = append(deleteUnitTypes, unit_model.TypePullRequests)