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:
authored and GitHub committed 2026-10-14 07:19:49 +00:00
1 parent aebdbd85fb
commit b015cd67e2
21 files changed
+600 -59

No files matched your search

+18 -1
View File
@@ -486,6 +486,16 @@ func doBranchProtectPRMerge(baseCtx *APITestContext, dstPath string) func(t *tes
t.Run("MergeProtectedToToforce", doGitMerge(dstPath, "protected"))
t.Run("PushToProtectedBranch", doGitPushTestRepository(dstPath, "origin", "toforce:protected"))
t.Run("CheckoutMasterAgain", doGitCheckoutBranch(dstPath, "master"))
t.Run("CreateBranchForProtectedDeletion", doGitCreateBranch(dstPath, "protected-delete"))
t.Run("PushBranchForProtectedDeletion", doGitPushTestRepository(dstPath, "origin", "protected-delete"))
t.Run("ProtectBranchWithoutDeletion", doProtectBranch(ctx, "protected-delete", baseCtx.Username, "", "", ""))
t.Run("DeleteProtectedBranchDenied", doGitPushTestRepositoryFail(dstPath, "origin", "--delete", "protected-delete"))
t.Run("ProtectBranchWithDeletionAllowlist", doProtectBranchExt(ctx, "protected-delete", doProtectBranchOptions{
UserToWhitelistPush: baseCtx.Username,
UserToWhitelistDelete: baseCtx.Username,
}))
t.Run("DeleteProtectedBranchAllowed", doGitPushTestRepository(dstPath, "origin", "--delete", "protected-delete"))
}
}
@@ -499,7 +509,7 @@ func doProtectBranch(ctx APITestContext, branch, userToWhitelistPush, userToWhit
}
type doProtectBranchOptions struct {
UserToWhitelistPush, UserToWhitelistForcePush, UnprotectedFilePatterns, ProtectedFilePatterns string
UserToWhitelistPush, UserToWhitelistForcePush, UserToWhitelistDelete, UnprotectedFilePatterns, ProtectedFilePatterns string
StatusCheckPatterns []string
}
@@ -529,6 +539,13 @@ func doProtectBranchExt(ctx APITestContext, ruleName string, opts doProtectBranc
formData["enable_force_push_allowlist"] = "on"
}
if opts.UserToWhitelistDelete != "" {
user, err := user_model.GetUserByName(t.Context(), opts.UserToWhitelistDelete)
assert.NoError(t, err)
formData["deletion_allowlist_users"] = strconv.FormatInt(user.ID, 10)
formData["enable_deletion"] = "whitelist"
}
if len(opts.StatusCheckPatterns) > 0 {
formData["enable_status_check"] = "on"
formData["status_check_contexts"] = strings.Join(opts.StatusCheckPatterns, "\n")