Protected Branches & Rulesets

Branch protection rules are essential for maintaining code quality and preventing mistakes. Learn how to protect your main branches, require status checks, enforce linear history, and set up rulesets for your repository.

Protected Branch Branch Ruleset Status Checks
What Are Protected Branches?

Protected branches are a security feature in GitHub that prevents developers from accidentally pushing directly to critical branches like main or master. Instead of allowing direct pushes, protection rules enforce that all changes must go through pull requests. This creates a natural checkpoint where code can be reviewed, tested, and discussed before it becomes part of the main codebase.

Without branch protection, it's easy for someone to accidentally push a broken commit directly to main, potentially breaking the build for everyone on the team. With protection enabled, you create a safety net. Even team members with write access cannot bypass the review process. This is especially important for production branches and long-lived integration branches where stability is critical.

Branch protection is not about restricting access—it's about establishing a workflow that ensures quality and consistency. Team members still have the ability to contribute; they just do so through pull requests rather than direct pushes.
Why Protect Branches?

Branch protection serves multiple critical purposes in a professional development workflow. First and foremost, it prevents mistakes. We've all had that moment where we accidentally pushed to the wrong branch or committed something that broke the build. Protected branches eliminate this risk entirely by blocking direct pushes entirely.

Beyond preventing accidents, branch protection enforces a consistent code review process. When every change to main must go through a pull request, you guarantee that no code enters production without being reviewed by at least one other developer. This dramatically improves code quality, spreads knowledge across the team, and catches bugs before they reach users.

Protected branches also integrate with your CI/CD pipeline. You can require that all automated tests pass before a pull request can be merged. This ensures that broken code never reaches your main branch, keeping your repository in a constantly deployable state. For teams practicing continuous deployment, this is essential.

Common Branch Protection Rules

GitHub offers several types of branch protection rules that you can enable individually. The most commonly used rule is Require a pull request before merging. This prevents direct pushes and requires all changes to be submitted via pull request. You can also require that pull requests have at least one approving review before they can be merged, and you can optionally dismiss approvals when new commits are pushed.

Require status checks is another critical rule. This forces your CI/CD pipeline to pass before merging. You can specify which status checks must succeed—for example, unit tests, linting, integration tests, or security scanning. If any of these checks fail, the merge button stays disabled until the issues are fixed.

Require linear history prevents merge commits and forces a clean, linear commit history. This is particularly useful for teams that prefer rebasing over merging. When enabled, pull requests must be rebased onto the target branch rather than merged, eliminating merge commits and creating a cleaner history.

Restrict who can push to matching branches allows you to limit which users or teams have the ability to push to protected branches. Even with protection enabled, you can still allow specific administrators or automation accounts to have bypass permissions when necessary.

# Example status checks that teams often require:
- CI / tests (passing all unit and integration tests)
- CodeQL / security analysis (no critical vulnerabilities)
- Lint (code style compliance)
- Build (application compiles successfully)
How to Set Up Branch Protection

Setting up branch protection in GitHub is straightforward. Navigate to your repository, then click on Settings > Branches. Under "Branch protection rules," click Add rule. In the "Branch name pattern" field, enter the name of the branch you want to protect. For the main branch, you would typically enter main or master. You can also use patterns like release/* to protect all release branches.

Next, check the protection rules you want to enable. At minimum, most teams enable "Require a pull request before merging" and "Require status checks to pass before merging." If you have automated tests set up, you'll need to specify which status checks are required. GitHub will show you a list of available status checks from your repository's actions.

Once you've configured your rules, click Create. From that moment forward, the branch is protected. Anyone trying to push directly will receive an error message. Pull requests targeting that branch will display a checklist of required reviews and status checks, and the merge button will remain disabled until all conditions are satisfied.

You can create multiple protection rules for different branch patterns. For example, you might protect main with strict rules, while protecting develop with slightly relaxed requirements. This gives you flexibility while maintaining safety where it matters most.
Branch Rulesets: The Next Generation

GitHub has introduced a new feature called Rulesets that extends and improves upon traditional branch protection. While branch protection rules apply only to specific branch names, rulesets can be configured with much more flexibility. You can create rules that apply to multiple branches, tags, or even entire repositories. Rulesets also allow for more granular control over who can bypass rules and under what conditions.

Rulesets can include multiple rules in a single configuration. For example, you might create a ruleset called "Production Protection" that applies to all branches matching main, release/*, and production/*. Within that ruleset, you can configure requirements for pull requests, status checks, and even restrict the types of files that can be changed in certain branches.

Rulesets also support bypass lists more flexibly than traditional protection. You can specify users, teams, or apps that are allowed to bypass specific rules. This is useful for deployment automation, where you might want a CI/CD bot to be able to push to production branches without requiring manual review, while still preventing developers from doing so.

Rulesets are gradually replacing traditional branch protection. While both are currently available, new repositories may default to rulesets. The concepts are similar, but rulesets offer more power and flexibility for complex workflows.
Require Linear History

Linear history is a Git feature that prevents merge commits and forces a clean, straightforward commit history. When you enable "Require linear history" on a protected branch, GitHub will reject any pull request that cannot be merged using a fast-forward strategy. This means that instead of creating a merge commit (which records the fact that two branches were merged), developers must rebase their feature branch onto the target branch.

Why would you want linear history? A linear commit history is easier to read and understand. Instead of a tangled web of merge commits, you see a single chronological line of changes. This makes it much easier to trace when a specific change was introduced, and it simplifies tasks like reverting changes or debugging with tools like git bisect.

Teams that adopt linear history typically use rebasing workflows. Instead of merging feature branches, they rebase onto main and then fast-forward. This eliminates the merge commits entirely. The trade-off is that rebasing rewrites commit history, which can be problematic for shared branches. For this reason, linear history is usually applied only to long-lived branches like main and develop.

When linear history is required, pull requests must be rebased before merging. GitHub provides a "Rebase and merge" button that handles this automatically, ensuring a clean history without manual rebasing.
Best Practices for Branch Protection

When setting up branch protection, start with your main production branch. This should have the strictest rules: require pull requests, require at least one approving review, require all status checks to pass, and require linear history. This ensures that only reviewed, tested code reaches production.

Consider protecting your development branch as well, but perhaps with slightly relaxed rules. For example, you might require pull requests but not require linear history, or require status checks but allow the author to merge their own PR. This balances safety with development velocity.

Make sure your CI/CD pipeline is reliable before requiring status checks. If tests are flaky or slow, requiring them to pass will create frustration. Invest in making your tests fast and stable first. Also, consider requiring security scanning tools like CodeQL as part of your status checks to catch vulnerabilities automatically.

Finally, document your branch protection strategy. Make sure all team members understand why branches are protected and how the workflow works. When people understand the reasoning behind the rules, they're more likely to follow them enthusiastically rather than seeing them as obstacles.

Teams that implement thoughtful branch protection consistently report fewer production incidents and higher confidence in their deployment process. It's one of the highest-ROI investments you can make in your development workflow.
Frequently Asked Questions
Can I still push to a protected branch if I have admin permissions?
Yes, repository administrators can bypass branch protection rules if they have the "Allow specified actors to bypass" setting configured. However, best practice is to treat admin permissions as an exception, not the rule. Even administrators should generally follow the same pull request workflow to maintain quality and visibility.
What happens if my CI pipeline takes a long time to run?
You can configure branch protection to require status checks, but you can also mark some checks as optional. Consider splitting your pipeline into fast checks (linting, formatting) that run immediately, and slower checks (integration tests, security scans) that run in parallel. Required checks should be stable and reasonably fast to avoid slowing down development.
Can I protect branches with wildcard patterns?
Yes! Branch protection supports glob patterns. For example, you can protect all branches starting with "release/" by using the pattern release/*. This is incredibly useful for teams that follow a consistent naming convention for feature branches or release branches.
What's the difference between "Dismiss stale pull request approvals" and not dismissing them?
When you enable "Dismiss stale pull request approvals," any approvals on a PR will be automatically removed if new code is pushed. This ensures that reviewers have a chance to review new changes. Without this setting, an approval remains valid even if the author makes significant changes after the review, potentially allowing unreviewed code to be merged.
How do I handle emergency hotfixes that need to bypass protection?
Best practice is to have a documented process for emergencies. This might involve temporarily relaxing protection for a specific branch, or using a dedicated hotfix process that still requires review but with expedited review times. Some teams create a separate "hotfix" branch with slightly relaxed protection rules, and then merge that into main after the fix is verified.
Can I require multiple reviewers before merging?
Yes. In the branch protection settings, you can specify the number of approving reviews required before merging. For critical branches, you might require two or more reviewers. This is especially common for security-sensitive code or architectural changes that need broad consensus.
What happens if I rename a branch that has protection rules?
Branch protection rules are based on branch name patterns. If you rename a branch, you'll need to update the protection rule to match the new name. The rule won't automatically transfer to the new branch name.
How do I test branch protection without affecting production?
Consider creating a test branch with protection rules first, or use a separate test repository to experiment with different protection configurations. Once you're confident in the settings, apply them to your main branches. You can also gradually introduce protections—start with requiring PRs, then add status checks after your CI pipeline is stable.
Previous: Code Review Best Practices Next: GitHub Actions

Protect your branches, protect your code quality, and ship with confidence.