mirror of
https://github.com/go-gitea/gitea.git
synced 2026-10-23 21:04:55 +02:00
feat(repo): allow deleting protected branches (#39243)
This adds a Branch Deletion setting with three modes: disabled by default, anyone with push access, or a restricted user/team/deploy-key allowlist. The policy applies to Git pushes, API/UI deletion, and post-merge cleanup; the default branch and the pull request target branch remain undeletable. Closes https://github.com/go-gitea/gitea/issues/33305 --------- Co-authored-by: Enoque Duarte <12515551+enoquefcd@users.noreply.github.com> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
21 files changed
+600
-59
No files matched your search
@@ -50,7 +50,7 @@ func Branches(ctx *context.Context) {
|
||||
|
||||
kw := ctx.FormString("q")
|
||||
|
||||
defaultBranchOptional, branches, branchesCount, err := repo_service.LoadBranches(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, optional.None[bool](), kw, page, pageSize)
|
||||
defaultBranchOptional, branches, branchesCount, err := repo_service.LoadBranches(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, ctx.Doer, ctx.Repo.Permission, optional.None[bool](), kw, page, pageSize)
|
||||
if err != nil {
|
||||
ctx.ServerError("LoadBranches", err)
|
||||
return
|
||||
|
||||
@@ -39,6 +39,7 @@ import (
|
||||
issue_service "gitea.dev/services/issue"
|
||||
"gitea.dev/services/notifications"
|
||||
pull_service "gitea.dev/services/pull"
|
||||
repo_service "gitea.dev/services/repository"
|
||||
user_service "gitea.dev/services/user"
|
||||
)
|
||||
|
||||
@@ -900,14 +901,15 @@ func (prInfo *pullRequestViewInfo) prepareMergeBox(ctx *context.Context, issue *
|
||||
return
|
||||
}
|
||||
if perm.CanWrite(unit.TypeCode) {
|
||||
// Check if branch is not protected
|
||||
if pull.HeadBranch != pull.HeadRepo.DefaultBranch {
|
||||
if protected, err := git_model.IsBranchProtected(ctx, pull.HeadRepo.ID, pull.HeadBranch); err != nil {
|
||||
log.Error("IsProtectedBranch: %v", err)
|
||||
} else if !protected {
|
||||
canDelete = true
|
||||
ctx.Data["DeleteBranchLink"] = issue.Link() + "/cleanup"
|
||||
if err := repo_service.CanDeleteBranchWithPermission(ctx, pull.HeadRepo, pull.HeadBranch, ctx.Doer, perm); err != nil {
|
||||
if errors.Is(err, util.ErrPermissionDenied) {
|
||||
log.Trace("CanDeleteBranch: %v", err)
|
||||
} else {
|
||||
log.Error("CanDeleteBranch: %v", err)
|
||||
}
|
||||
} else {
|
||||
canDelete = true
|
||||
ctx.Data["DeleteBranchLink"] = issue.Link() + "/cleanup"
|
||||
}
|
||||
canWriteToHeadRepo = true
|
||||
}
|
||||
|
||||
@@ -89,11 +89,13 @@ func SettingsProtectedBranch(c *context.Context) {
|
||||
|
||||
c.Data["PushUsers"] = pushUsers
|
||||
c.Data["ForcePushUsers"] = pushUsers
|
||||
c.Data["DeletionUsers"] = pushUsers
|
||||
c.Data["MergeUsers"] = repoUsers
|
||||
c.Data["BypassCheckUsers"] = repoUsers
|
||||
c.Data["ApprovalUsers"] = repoUsers
|
||||
c.Data["whitelist_users"] = strings.Join(base.Int64sToStrings(rule.WhitelistUserIDs), ",")
|
||||
c.Data["force_push_allowlist_users"] = strings.Join(base.Int64sToStrings(rule.ForcePushAllowlistUserIDs), ",")
|
||||
c.Data["deletion_allowlist_users"] = strings.Join(base.Int64sToStrings(rule.DeletionAllowlistUserIDs), ",")
|
||||
c.Data["merge_whitelist_users"] = strings.Join(base.Int64sToStrings(rule.MergeWhitelistUserIDs), ",")
|
||||
c.Data["bypass_allowlist_users"] = strings.Join(base.Int64sToStrings(rule.BypassAllowlistUserIDs), ",")
|
||||
c.Data["approvals_whitelist_users"] = strings.Join(base.Int64sToStrings(rule.ApprovalsWhitelistUserIDs), ",")
|
||||
@@ -110,6 +112,7 @@ func SettingsProtectedBranch(c *context.Context) {
|
||||
c.Data["Teams"] = teams
|
||||
c.Data["whitelist_teams"] = strings.Join(base.Int64sToStrings(rule.WhitelistTeamIDs), ",")
|
||||
c.Data["force_push_allowlist_teams"] = strings.Join(base.Int64sToStrings(rule.ForcePushAllowlistTeamIDs), ",")
|
||||
c.Data["deletion_allowlist_teams"] = strings.Join(base.Int64sToStrings(rule.DeletionAllowlistTeamIDs), ",")
|
||||
c.Data["merge_whitelist_teams"] = strings.Join(base.Int64sToStrings(rule.MergeWhitelistTeamIDs), ",")
|
||||
c.Data["bypass_allowlist_teams"] = strings.Join(base.Int64sToStrings(rule.BypassAllowlistTeamIDs), ",")
|
||||
c.Data["approvals_whitelist_teams"] = strings.Join(base.Int64sToStrings(rule.ApprovalsWhitelistTeamIDs), ",")
|
||||
@@ -171,7 +174,7 @@ func SettingsProtectedBranchPost(ctx *context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
var whitelistUsers, whitelistTeams, forcePushAllowlistUsers, forcePushAllowlistTeams, mergeWhitelistUsers, mergeWhitelistTeams, approvalsWhitelistUsers, approvalsWhitelistTeams, bypassAllowlistUsers, bypassAllowlistTeams []int64
|
||||
var whitelistUsers, whitelistTeams, forcePushAllowlistUsers, forcePushAllowlistTeams, deletionAllowlistUsers, deletionAllowlistTeams, mergeWhitelistUsers, mergeWhitelistTeams, approvalsWhitelistUsers, approvalsWhitelistTeams, bypassAllowlistUsers, bypassAllowlistTeams []int64
|
||||
protectBranch.RuleName = f.RuleName
|
||||
if f.RequiredApprovals < 0 {
|
||||
ctx.Flash.Error(ctx.Tr("repo.settings.protected_branch_required_approvals_min"))
|
||||
@@ -215,6 +218,29 @@ func SettingsProtectedBranchPost(ctx *context.Context) {
|
||||
protectBranch.EnableForcePushAllowlist = false
|
||||
}
|
||||
|
||||
enableDeletion := f.EnableDeletion
|
||||
if !protectBranch.CanPush {
|
||||
// Deletion requires push access.
|
||||
enableDeletion = ""
|
||||
}
|
||||
switch enableDeletion {
|
||||
case "all":
|
||||
protectBranch.CanDelete = true
|
||||
protectBranch.EnableDeletionAllowlist = false
|
||||
case "whitelist":
|
||||
protectBranch.CanDelete = true
|
||||
protectBranch.EnableDeletionAllowlist = true
|
||||
if strings.TrimSpace(f.DeletionAllowlistUsers) != "" {
|
||||
deletionAllowlistUsers, _ = base.StringsToInt64s(strings.Split(f.DeletionAllowlistUsers, ","))
|
||||
}
|
||||
if strings.TrimSpace(f.DeletionAllowlistTeams) != "" {
|
||||
deletionAllowlistTeams, _ = base.StringsToInt64s(strings.Split(f.DeletionAllowlistTeams, ","))
|
||||
}
|
||||
default:
|
||||
protectBranch.CanDelete = false
|
||||
protectBranch.EnableDeletionAllowlist = false
|
||||
}
|
||||
|
||||
protectBranch.EnableMergeWhitelist = f.EnableMergeWhitelist
|
||||
if f.EnableMergeWhitelist {
|
||||
if strings.TrimSpace(f.MergeWhitelistUsers) != "" {
|
||||
@@ -288,6 +314,8 @@ func SettingsProtectedBranchPost(ctx *context.Context) {
|
||||
TeamIDs: whitelistTeams,
|
||||
ForcePushUserIDs: forcePushAllowlistUsers,
|
||||
ForcePushTeamIDs: forcePushAllowlistTeams,
|
||||
DeletionUserIDs: deletionAllowlistUsers,
|
||||
DeletionTeamIDs: deletionAllowlistTeams,
|
||||
MergeUserIDs: mergeWhitelistUsers,
|
||||
MergeTeamIDs: mergeWhitelistTeams,
|
||||
ApprovalsUserIDs: approvalsWhitelistUsers,
|
||||
|
||||
Reference in new issue
Block a user