Veridical11 min readArticle

7 Steps to Stop Merge Blockers with Branch Protection Rules for Dev Teams

7 Steps to Stop Merge Blockers with Branch Protection Rules for Dev Teams

Geometric gates representing protected code merges

Branch protection rules prevent direct pushes and require pull requests and passing checks before merging code into a critical branch. The simplest secure default is PR-only merges on your default branch, paired with at least one required reviewer and required status checks. From there, the right level of rigor depends on team size, release cadence, and how much risk a bad merge actually carries.


TL;DR:

  • Required checks must pass on the latest commit and within seven days; rerun stale checks, and use stable, unique job names to prevent silent failures.
  • Organization and repository rulesets stack, with the strictest matching requirement winning, so check overlapping policies when a merge is unexpectedly blocked.
  • A team of five can start with one reviewer and a test check, while production billing or security changes warrant two reviewers.
  • Add protections in stages: enforce pull request merges and one required check, then track merge time and backlog for two weeks before adding more rules.

Veridical
veridical.dev
Add Evidence to Your PR Reviews
Veridical reviews GitHub pull requests with verified findings, detailed summaries, and inline evidence to help teams catch critical defects before merging.
Visit Veridical

Table of Contents

What branch protection rules do and when to use them

Protections exist to stop risky changes from reaching code that other people depend on. Without them, anyone with write access can push straight to main, force-push over history, or delete a branch outright, and none of that requires a second pair of eyes. Once protections are on, those actions are blocked unless they go through a pull request that satisfies the rules you set.

Most teams protect a small set of branches rather than everything in the repository:

  • The default branch (usually main), since it typically feeds production deployments.
  • develop or integration branches, when a team uses a longer-lived staging line before release.
  • release/* branches, so a shipped version cannot be altered without review.

Protections also give continuous integration a job to do beyond reporting, supported by effective deployment and MLOps services. When a status check is required, a failing build or test suite does not just show a red X, it physically blocks the merge button. That single mechanical link between CI and merge eligibility is what keeps a deployable branch deployable.

Core settings explained: what each protection enforces

Each protection setting solves a specific failure mode, and each carries a trade-off between safety and merge speed.

  • Require pull request reviews forces at least one approving review before merge; setting a reviewer count above one adds confidence but also adds wait time, and enabling “dismiss stale reviews” re-triggers approval whenever new commits land, which catches last-minute changes but can frustrate reviewers who approved twice already.
  • Require status checks to pass ties merge eligibility to CI results, but only on specific terms worth knowing cold.
  • Require signed commits verifies author identity cryptographically, which matters for compliance-heavy teams but adds setup friction for anyone new to GPG or SSH signing.
  • Restrict who can push limits direct write access to a named list of people or teams, closing the gap that PR-only rules alone do not, since push restrictions and PR requirements address different attack surfaces.
  • Lock branch freezes a branch entirely, useful for archived release lines that should never change again.
  • Include administrators decides whether repository admins are bound by the same rules as everyone else; leaving it off quietly creates a second, unprotected path into the branch.

The status check rule has sharp edges. Required checks must pass on the latest commit SHA and must have completed successfully within the past seven days to remain valid, per GitHub’s troubleshooting documentation. A check that passed eight days ago on an older commit will not satisfy the rule today, even if nothing in the code has changed. This is the single most common cause of a PR that “should” be mergeable but is not.

Signed commits roll out most smoothly when a team agrees on tooling first. Git’s own documentation on commit signing covers the setup, and running git config --local commit.gpgsign true across the team before flipping the requirement avoids a wave of rejected pushes on day one.

Rulesets vs classic branch protection: how layering works

GitHub offers two mechanisms for the same goal: classic branch protection rules, scoped to one branch pattern at a time, and rulesets, which can target multiple branches or tags and stack across a repository or an organization. GitHub’s documentation on rulesets states that rulesets aggregate every rule that matches a given branch, and when more than one rule applies to the same setting, the most restrictive version wins.

That aggregation behavior has practical consequences:

  • A repository-level ruleset requiring one reviewer and an organization-level ruleset requiring two will enforce two, since the stricter rule always takes priority.
  • Branch targeting uses fnmatch-style patterns, and GitHub’s guide to managing protection rules notes that a wildcard like * will not match across slashes unless you use a recursive pattern, a detail that trips up anyone trying to match release/1.0 with a plain release/*.
  • Rulesets suit organizations managing protections across many repositories at once; classic rules still work fine for a single repository with simple needs.

Practical implementation checklist

Setting up protections well is mostly a matter of sequencing decisions correctly before you flip any switches.

  1. Pick the branches to protect and decide on naming patterns (main, release/*) before creating the rule, so targeting is deliberate rather than accidental.
  2. Enable PR-only merges by turning off direct pushes and requiring a pull request for all changes.
  3. Set a reviewer count, typically one for small teams and two for anything touching production billing or security code.
  4. Add required status checks by exact, unique name, since a renamed CI job will silently stop satisfying the rule.
  5. Decide on signed commits and roll the requirement out after the team has configured signing locally.
  6. Restrict push access to a named team rather than leaving it open to all collaborators.
  7. Choose the “include administrators” setting intentionally, not by default, and if your plan supports an Evaluate or dry-run mode, use it before enforcing.

A small team of five engineers typically needs one reviewer, one required check (the test suite), and no signed commits. An enterprise rollout usually adds two reviewers, CODEOWNERS-based approval, signed commits, and a restricted push list scoped to a release-engineering team.

Pro Tip: Name your required status checks something that will never change when you rename a CI job, like ci/tests instead of the workflow’s display name.

Troubleshooting common failures that block merges and pushes

Most blocked merges trace back to a handful of repeat offenders, and each has a specific fix.

  • Skipped checks: path filters, branch filters, or conditional job logic in the workflow file can leave a required check pending forever rather than failing it; GitHub’s troubleshooting guide walks through adjusting the trigger conditions.
  • Merge queue stuck pending: workflows that support a merge queue need the merge_group event as an explicit trigger, since without it the check tied to the queue never runs and the merge stays blocked indefinitely.
  • Unexpected check source: when an organization-level ruleset defines a required workflow, the run has to fire from an eligible event (push, pull_request, pull_request_target, deployment, or deployment_status) or the check never attaches to the PR at all.
Symptom Likely cause First fix to try
Check passed earlier but merge still blocked Check ran on an older commit or aged past 7 days Push a new commit or rerun the workflow
Required check never appears on the PR Workflow trigger event not eligible for the org ruleset Confirm the workflow fires on push or pull_request
Merge queue entry stuck pending Missing merge_group trigger in the workflow Add merge_group to the workflow’s on: block
Unsure which rule is blocking the merge Multiple rulesets or protections overlapping Check the ruleset Insights page for the repository

The Insights page for rulesets is often the fastest diagnostic step, since it shows exactly which rule evaluated and blocked a given push without needing admin access to check.

Best practices and developer habits that make protections reliable

Settings alone do not make a repository safe; the habits around them do. Keep the branch strategy itself simple: short-lived feature branches, pull requests into a default branch that stays deployable at all times, and release branches only when a longer-lived line is genuinely needed.

  • Write atomic commits with descriptive messages, since a tangled commit history makes flaky check failures harder to isolate and re-run.
  • Roll out a signed-commit policy only after the team has configured git config signing locally and had a short walkthrough, not as a surprise enforcement.
  • Keep bypass permissions narrow and tied to pull requests rather than wide open; GitHub’s ruleset documentation notes that scoping bypass “for pull requests only” still lets an admin override at merge time while preserving an audit trail.
  • Audit bypass usage periodically rather than assuming it is never used.
  • For release branches, settle on a consistent cherry-pick or port-forward strategy so hotfixes do not drift out of sync with main.

Pro Tip: Review your bypass list every quarter. Permissions granted during an incident have a habit of outliving the incident.

How evidence-based PR reviews complement branch protection

Branch protection rules enforce process: a review happened, a check passed. Neither confirms the pull request’s actual content is safe. That gap is where evidence-based review fits, surfacing concrete defects like data leaks or performance regressions inside the diff itself, with verified findings rather than a pass or fail process check. Teams layering this kind of review alongside required checks typically add it once PR-only merges and required reviewers are already stable.

Process checks and evidence review layers

Rollout sequencing: what I’d change first

Start with PR-only merges and one required check, then measure merge time and PR backlog for two weeks before adding reviewer counts or signed commits. Rigor added in waves is easier to debug than rigor added all at once. Audit bypass logs and CI health quarterly, and only then weigh where an evidence-based review layer earns its keep against the friction it adds.

— Łukasz

Reduce merge risk beyond process with Veridical

Branch protection rules stop an unreviewed change from landing, but they cannot tell you whether the reviewed change is actually safe. Our evidence-based PR review analyzes the diff itself, verifies findings against concrete evidence, and attaches a calibrated advisory score you can wire into your merge gate alongside your required checks.

Veridical

FAQ

What are branch protection rules in GitHub?

Branch protection rules are repository settings that restrict how a branch can be changed, commonly blocking direct pushes, force pushes, and deletions while requiring a pull request, reviews, and passing status checks before a merge is allowed. GitHub’s documentation on protected branches covers the full set of available options.

How do I list all branches in a repository?

Running git branch -a from the command line lists every local and remote-tracking branch, while the repository’s “Branches” tab on GitHub shows the same list with activity and protection status for each one.

How can I prevent merges to the main branch on GitHub?

Enable a branch protection rule or ruleset on the default branch that requires a pull request with at least one approving review and a passing required status check, which blocks any direct merge or push that skips that process.

How many branches does GitHub allow?

GitHub does not publish a fixed cap on the number of branches a repository can have, and in practice teams manage repositories with thousands of branches without hitting a platform limit; the more common constraint is keeping branch names and protection targeting organized as the count grows.