Overview
Branch rules keep main deployable: a direct push or a merge with failing checks can break the deploy. GitHub offers classic branch protection rules (Settings, Branches) and rulesets (Settings, Rules, Rulesets). This page picks one, lists the rules worth enabling, and names the bypass gaps. Branch strategy is in github.
Prefer rulesets for new repositories
Rulesets cover the common classic rules and add layering. Per GitHub’s documentation:
- Several rulesets can apply to one branch at once; where they conflict, the most restrictive rule wins. Only one classic rule applies per branch.
- A ruleset has an enforcement status (Active or Disabled), so you can test or pause it without deleting it.
- Anyone with read access can view the active rulesets.
- A repository can have up to 75 rulesets, and an organization up to 75 organization-wide rulesets.
- The bypass list takes repository roles, teams, and GitHub Apps.
Classic rules still work. Keep them on existing repositories until you migrate, and do not run both for the same branch without checking how they combine.
Require a pull request before merging
Enable “Require a pull request before merging” so a direct push to main is impossible for covered actors. Then set the review options:
- Required approvals: 1 for solo and small teams. A reviewer who reads diffs beats three who rubber-stamp.
- Dismiss stale approvals when new commits are pushed. GitHub dismisses a review when commits change the pull request’s diff.
- “Require approval of the most recent reviewable push” so the last commit is always covered by an approval.
- Require review from code owners when github-pr-workflow routes review through
CODEOWNERS. - Require conversation resolution before merge.
Require status checks
Name each required check by its exact job name from github-actions. Keep the required set small and reliable; a flaky required check blocks every PR. Leave informational checks (coverage, bundle size) optional.
Rulesets offer a strict mode that requires the branch to be up to date with the base before merging, and a loose mode that does not. Use strict when two PRs can pass CI separately and fail together; accept the cost of rebasing on a busy repository.
Block force pushes, deletions, and merge commits
- “Block force pushes” (on by default in rulesets) stops history rewrites that break other clones and in-flight CI.
- “Restrict deletions” (on by default) protects the branch itself.
- “Require linear history” rejects merge commits so every merge is a squash or rebase. Disable merge commits in repository settings too, or contributors hit confusing errors at merge time. See github-pr-workflow.
For automation that must push to main (changelog bots), add the bot to the bypass list instead of opening force pushes to everyone. Bypass is auditable; an open permission is not.
Require signed commits where identity matters
“Require signed commits” accepts only pushes whose commits are signed and verified. Use it for published packages, internal tooling, and supply-chain-sensitive repositories. Skip it for content repositories where it adds setup for every contributor and little protection.
Close the bypass gaps
- Classic rules exempt repository admins. Turn on “Do not allow bypassing the above settings”; it also applies to roles with the bypass permission.
- Classic push restrictions do not stop people and apps with admin permission; they can always push.
- Audit installed GitHub Apps and give admin permission only to apps that need it.
- Require MFA for organization owners, who can change the rules.