Branch Protection and Rulesets
Enforce reviews and passing checks before code can reach protected branches.
Beyond blocking direct pushes, branch protection lets you require quality gates on merges. Modern GitHub calls these Rulesets, which can target many branches by pattern.
Recommended rules for main
- Require a pull request with at least one approval.
- Require status checks (CI) to pass.
- Require branches to be up to date before merging.
- Require conversations to be resolved.
- Block force pushes and deletions.
These rules make it very hard to break the main branch by accident.
Example
# Enable protection via the API / gh CLI
gh api -X PUT repos/you/repo/branches/main/protection \
-F required_pull_request_reviews.required_approving_review_count=1 \
-F enforce_admins=true \
-F required_status_checks.strict=trueWhen to use it
- A startup protects main to require one PR approval and passing CI, preventing any single developer from merging untested code.
- An enterprise organisation applies a ruleset to every repository so all teams follow the same merge standards without per-repo configuration.
- A team enables 'Require branches to be up to date' so a PR cannot merge while main has moved ahead, eliminating stale-merge bugs.
More examples
Set branch protection via REST API
Applies protection to main: CI must pass and one reviewer must approve before any PR can merge.
gh api repos/{owner}/{repo}/branches/main/protection \
--method PUT \
--input - << 'JSON'
{
"required_status_checks": {
"strict": true,
"contexts": ["ci/tests"]
},
"required_pull_request_reviews": {
"required_approving_review_count": 1
},
"enforce_admins": false,
"restrictions": null
}
JSONCreate a fine-grained org ruleset
Creates an organisation-level ruleset targeting the default branch of every repo, enforcing PR and status check requirements fleet-wide.
gh api orgs/{org}/rulesets \
--method POST \
-f name="protect-main" \
-f target="branch" \
-f enforcement="active" \
-f "conditions.ref_name.include[]"="~DEFAULT_BRANCH" \
-f "rules[0].type"="pull_request" \
-f "rules[1].type"="required_status_checks"Verify protection is active
Reads the current protection rules and extracts the review count and required check names for a quick audit.
gh api repos/{owner}/{repo}/branches/main/protection \
--jq '{
required_reviews: .required_pull_request_reviews.required_approving_review_count,
required_checks: .required_status_checks.contexts
}'
Discussion