Free live AI batch is about to begin — Express your interest →

GitHub Branch Protection Rules vs Rulesets: Why and How to Use Them

Published · Updated

Short answer. For a new repository, use a branch ruleset. Several rulesets can apply to the same branch at once, anyone with read access can see what is enforced, and you can switch one off without deleting it. A classic branch protection rule still works and is enforced alongside any rulesets, but only one classic rule applies to a branch at a time. If you already have classic rules, GitHub can convert them for you.

Either way, protection does not prove code is correct. It enforces a chosen path for changing a branch — pull request, review, passing checks — so those habits stop depending on everyone remembering them.

Rulesets vs classic branch protection, side by side

Question Classic branch protection rule Branch ruleset
How many apply to one branch? Only one at a time; with overlapping patterns, a specific branch name wins, then the oldest rule Every matching ruleset applies; where two define the same rule, the most restrictive version wins
Who can see the rules? Repository admins, in Settings Anyone with read access to the repository
Can I pause it? Only by editing or deleting it Yes — change its enforcement status to Disabled
Who is exempt by default? Admins (and roles with the “bypass branch protections” permission), unless you tick Do not allow bypassing the above settings Nobody, unless you add them to the bypass list
Where is it in Settings? Branches → Add classic branch protection rule Rules → Rulesets → New ruleset

Both mechanisms can protect the same repository. GitHub’s About rulesets page states that they work alongside each other and all applicable rules are enforced, so a change must satisfy both. That is the most common source of confusion when a repository has been protected twice by different people.

Rulesets also reach further than the classic form: a branch ruleset can require code-scanning results before merging, and on the Team plan a separate push ruleset can block files by path, extension or size in private repositories. The full list is in Available rules for rulesets; several depend on your plan and on other GitHub features being enabled.

Before you start: plan and permission

  • Plan and visibility. Both protected branches and rulesets are available in public repositories on GitHub Free, and in public and private repositories on GitHub Pro, Team and Enterprise Cloud. On a private repository under a Free account, neither is available. Check the “Who can use this feature?” note on GitHub’s about protected branches page before planning around a feature.
  • Permission. You need admin access to the repository, or a custom role with the “edit repository rules” permission.
  • Evaluate mode is not everywhere. GitHub documents a third enforcement status, Evaluate, that records what a ruleset would have blocked without blocking it. It belongs to the Enterprise feature set; on other plans you choose between Active and Disabled.

A practical baseline for main

Start with the smallest policy your repository can actually satisfy:

  1. Require a pull request before merging.
  2. Require at least one approving review when a real independent reviewer exists.
  3. Dismiss stale approvals when new commits change the reviewed diff.
  4. Require conversation resolution.
  5. Require the specific CI checks that run on every pull request (see the trap below).
  6. Block force pushes and branch deletion.
  7. Decide who, if anyone, may bypass — and write the reason down.

Solo projects need judgment. An approval requirement that the only maintainer cannot satisfy blocks all work. A solo maintainer can still require pull requests, conversation resolution and passing checks to keep a reviewable change record, then raise the approval count when collaborators join.

Create a branch ruleset

From the repository, open Settings. Under “Code, planning, and automation”, click Rules, then Rulesets, then New ruleset → New branch ruleset.

  1. Name it after its intent — “Default branch: review and CI” says more in an audit than “Rule 1”.
  2. Set the enforcement status. A new ruleset starts as Disabled. Change it to Active before you create it, or it protects nothing.
  3. Add bypass actors deliberately (see the bypass section). An empty bypass list means the rules apply to everyone, administrators included.
  4. Choose targets. Under “Target branches”, Add a target. Include the default branch explicitly rather than a broad fnmatch pattern until you have checked what the pattern matches; you can combine includes and excludes in one ruleset.
  5. Pick the rules. Restrict deletions and Block force pushes are selected by default — leave them on. Add Require a pull request before merging (with the approval count, stale-approval dismissal and conversation resolution) and Require status checks to pass.
  6. Click Create. An Active ruleset takes effect immediately.

The steps follow GitHub’s creating rulesets for a repository; its labels are the source of truth when the interface changes.

Or configure a classic branch protection rule

From Settings, click Branches, then Add classic branch protection rule. Enter the branch name or pattern; the branch does not have to exist yet. Then select Require a pull request before merging (and its approval options), Require status checks to pass before merging, Require conversation resolution before merging, and, if you want admins held to the same rules, Do not allow bypassing the above settings. Force pushes and deletions are blocked on a protected branch unless you enable them. Click Create.

GitHub’s managing a branch protection rule page has the full option list, including signed commits, linear history, merge queue and branch lock.

Moving from classic rules to rulesets

You do not have to rebuild protection by hand. Under Settings → Branches, each classic rule has a Convert to ruleset action that generates rulesets preserving its behaviour, lets you review the “New behavior” summary, and optionally deletes the original rule once the conversion is done.

One setting does not map one to one: in classic rules, “Require conversation resolution before merging” stands alone; in rulesets it is part of the pull request rule and only applies when that rule is enabled. GitHub’s converting branch protections to rulesets page covers the details.

If you keep the classic rule after converting, both stay enforced. Delete the old rule once you have tested the ruleset, so there is one place to read the policy.

The required-status-check trap

Required checks are where most protection setups quietly break. GitHub’s troubleshooting required status checks page documents the causes:

  • A check you can select must have run recently. A required status check must have completed successfully in the repository during the past seven days. A brand-new workflow has to run once before you can require it.
  • A skipped workflow blocks merging forever. If a workflow is skipped by path filtering, branch filtering or a skip instruction in the commit message, its checks stay Pending and the pull request cannot merge. Do not require a workflow that can be skipped.
  • A skipped job may not block at all. A job skipped by a conditional reports success, and a job that depends on a failed job is skipped and may not block merging. Use always() with needs for a required job that depends on others.
  • Renames break the link. The required check is matched by name. Rename a job or workflow and pull requests wait for a check that will never report. Update the rule in the same change that renames the job.
  • “Require branches to be up to date” is the strict mode: every pull request must include the latest main before merging. It is safer and costs more CI runs; leaving it off means two individually green pull requests can still break main together.

Bypass, force push and deletion: decide before an incident

Bypass exists for real emergencies — a broken deployment, a CI outage — but an unrestricted escape route weakens everything above it.

  • In a ruleset, only the actors on the bypass list can bypass: repository admins, organization owners, the maintain or write role, specific teams, GitHub Apps or Dependabot. For each actor you can choose Always allow or For pull requests only; the second still forces them through a pull request, which leaves a record of what was merged and why.
  • In a classic rule, admins bypass by default. Turn on Do not allow bypassing the above settings to remove that. GitHub notes that actors can only be added to a classic rule’s bypass lists when the repository belongs to an organization.
  • Keep force pushes and deletion blocked on protected branches. Never use a force push as a routine way around a failing check: fix the change or fix the check. If a credential entered history, rotate it first — branch protection is not secret remediation.

Write down which incidents justify a bypass, who reviews it afterwards, and where the reason is recorded.

Test the rule instead of assuming

Create a harmless topic branch and pull request, then confirm each control you expect:

  • a direct push to the protected branch is rejected;
  • the pull request cannot merge before the required approval;
  • a failing required check blocks the merge;
  • a new commit dismisses an approval, if configured;
  • an unresolved review conversation blocks the merge, if configured;
  • someone not on the bypass list cannot bypass;
  • deleting or force pushing the branch is refused;
  • the emergency actor can perform only the intended bypass.

Record the result in the repository’s contributor documentation, and re-test after renaming CI jobs, changing plans, transferring the repository or changing the default branch.

Protection works with a clear workflow

Rules make more sense once the team shares the same vocabulary. Review repositories, branches and pull requests, then practise creating a repository and managing a PR. For a concrete first repository, use the guide to link a Spring Boot project to GitHub.

These governance habits are also part of the delivery and client-environment responsibilities in the Forward-Deployed Engineer program.