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

Example · bash
# 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=true

When 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.

Example · bash
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
}
JSON

Create 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.

Example · bash
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.

Example · bash
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

  • Be the first to comment on this lesson.