Collaborating on GitHub
Topic 37

Branch Protection and Rulesets

Collaboration

Branch protection and rulesets enforce what must be true before code reaches a protected branch — required reviews, passing checks, a linear history — turning team conventions into mechanisms the merge button actually obeys. They are the difference between "we agreed to always review" and "the platform will not let this merge unreviewed."

The classic per-branch protection settings still work, but rulesets are the newer model that layers, targets tags as well as branches, and can run in a preview mode before they start blocking. Knowing which one you are configuring — and that the strictest applicable rule wins — is what keeps enforcement predictable.

What protection enforces, from top layer down to foundation
Required reviews + CODEOWNERS
approvals from the right people before merge
Required status checks + up-to-date
checks must pass against current base
Block force-push & deletion
history cannot be rewritten or removed
Protected branch
the branch the rules apply to

What Protection Enforces

A protected branch can require passing status checks, a minimum number of approving reviews, and that approvals be dismissed when new commits arrive. It can require the branch to be up to date with its base before merging, restrict who is allowed to push at all, and block force-pushes and branch deletion outright.

Each of these maps to a concrete failure it prevents: unreviewed code, code that never ran CI, and history rewrites or deletions on the branch everyone depends on.

Rulesets vs Classic Branch Protection

Classic branch protection is one rule per branch pattern: simple, long-established, but it does not stack and cannot target tags. Rulesets can layer — several may apply to the same branch at once — target tags and pushes, run in "evaluate" mode to report what they would block before they enforce, and expose an explicit bypass list. For new repositories and anything org-wide, rulesets are the better choice, and the two can coexist with the strictest applicable rule winning.

Required Status Checks

A required check is identified by its exact check name, and that exactness matters: a required check whose name does not match any job that actually runs simply never triggers, so the rule does nothing and unchecked code merges. "Required" is also distinct from merely "present" — a check can run and report without being required to pass. Pairing required checks with "branch up to date before merge" closes the gap where two independently green pull requests merge into a broken combined state.

Linear History and Signed Commits

Protection can forbid merge commits to keep history linear, require that commits be signed so authorship is verifiable, and require that a deployment to a named environment succeed before merge. These turn properties you would otherwise have to police by review into hard preconditions.

Bypass and Enforcement

Rulesets carry an explicit bypass list naming who may skip them, which is far clearer than the classic "allow administrators to bypass" toggle. That toggle is a common trap: the admin under deadline pressure is exactly the person who should not merge around review. Evaluate mode and ruleset insights let you see the impact of a rule before it starts blocking real pull requests.

Classic Branch Protection vs Rulesets

Classic branch protection — one rule per branch pattern. Simple and long-established, but it does not stack and cannot target tags. Fine for a single repo with one straightforward policy.

Rulesets — multiple can layer over the same branch, they target tags and pushes, run in "evaluate" mode before enforcing, and expose a clear bypass list. Choose rulesets for new setups and anything org-wide; both can coexist, and the strictest applicable rule wins.

Common Mistakes
  • Requiring a status check by a name that does not exactly match the job's check name — the rule silently never triggers and unchecked code merges.
  • Leaving "allow administrators to bypass" enabled — the admin under deadline pressure, who most needs review, merges straight to main.
  • Requiring approvals but not "dismiss stale approvals on new commits" — an approved PR gains new unreviewed commits and merges anyway.
  • Forgetting to require "branch up to date before merge" — two PRs that pass individually merge into a main broken by a semantic conflict.
  • Switching on an org-wide ruleset in active mode with no evaluate pass first — every open PR is blocked at once and the team escalates.
Best Practices
  • Prefer rulesets over classic protection for new repos, and run them in "evaluate" mode first to see the impact before enforcing.
  • Require status checks by their exact check name and require the branch to be up to date with its base.
  • Disable admin bypass on main so no one can merge around review under pressure.
  • Enable "dismiss stale approvals on new commits" alongside required reviews.
  • Require linear history and block force-pushes and deletions on the default branch.
Comparable toolsGitLab protected branches and push rulesBitbucket branch permissions and merge checksAzure DevOps branch policiesGitea branch protection (a subset)

Knowledge Check

Why is a required status check with a misspelled name worse than no check at all?

  • It never matches a real job, so the rule silently never fires and people merge believing checks are enforced
  • GitHub blocks every merge to that branch until an admin corrects the spelling in settings
  • It runs the nearest alphabetically similar job and reports its result as a false pass
  • It doubles every PR's CI time, because GitHub schedules the misspelled check twice on each push to the branch

What can rulesets do that classic branch protection cannot?

  • Layer multiple rules over one branch, target tags and pushes, and run in evaluate mode first
  • Require a set number of approving reviews, which classic protection has never been able to enforce
  • Block force-pushes to the default branch, a guarantee only rulesets can provide
  • Apply selectively by branch-name pattern, which classic per-branch rules cannot do

Which setting catches two PRs that each pass alone but break main together?

  • Requiring the branch to be up to date with its base before merge, which re-runs checks on the combined state
  • Requiring signed commits so each change is verified before it can merge into the branch
  • Requiring linear history so the two PRs land as a clean rebased sequence on top of each other rather than colliding
  • Blocking force-pushes on the default branch so neither PR can overwrite the other's commits

Why disable admin bypass on main?

  • The admin under deadline pressure is exactly who should not merge around review, so bypass defeats the policy
  • Admins cannot run the required status checks on their own commits while bypass is on
  • Leaving bypass on permanently disables branch protection for every contributor across the whole repo, dropping all required checks and reviews at once
  • GitHub charges an extra per-seat fee on the organization whenever admin bypass stays enabled on a protected branch

You got correct