skip to content

If two GitHub rulesets and a branch protection rule target main, which rules apply?

level: seniorimportance: should knowfreq 40%

answer

  1. not a winner — a collection
  2. think union, not override
  3. strictest constraint survives
  4. exempt from which source, exactly
  5. ask the API for effective rules

basics

~20 s

All of them. GitHub aggregates every rule from every applicable ruleset and from classic branch protection, and where they overlap the most restrictive setting wins. There is no first-match precedence and no way for one to relax another.

solid answer

~40 s

GitHub evaluates the **union**, not a winner. Every ruleset whose conditions match the ref contributes its rules, classic branch protection contributes its settings, and the effective policy is all of them together; where two express the same constraint differently — one requires 1 approval, another requires 2 — the **most restrictive** applies. Rulesets whose enforcement status is `disabled` contribute nothing, and one in `evaluate` mode records violations without blocking. Bypass does not propagate: being a bypass actor on one ruleset exempts you from *that* ruleset's rules only, so another ruleset or the classic rule can still block you. The practical consequence is that you cannot debug this by reading one configuration screen — query the effective rules for the branch (`GET /repos/{owner}/{repo}/rules/branches/{branch}`), which lists each active rule together with the ruleset it came from.

code

bash · 4 lines
bash
curl -sS \
  -H "Accept: application/vnd.github+json" \
  -H "Authorization: Bearer $GITHUB_TOKEN" \
  https://api.github.com/repos/acme/payments-service/rules/branches/main

go deeper

for a junior

Recall that more than one policy can protect the same branch and that they add up rather than replacing each other, so the strictest requirement is the one you feel.

for a middle

Explain the aggregation: every active applicable ruleset plus classic protection contributes rules, overlapping constraints resolve to the most restrictive, and disabled rulesets contribute nothing.

for a senior

Diagnose a blocked merge without guessing — query the branch's effective rules, identify the contributing ruleset, and check whether any bypass entry actually covers the rule that fired.

for a principal

Own how many layers your organization tolerates, how teams add strictness without being able to subtract it, and how a policy migration is staged so no branch is ever briefly unprotected.

## The evaluation model When a push arrives or a merge is attempted, GitHub collects every policy that targets the ref in question: 1. Each **repository ruleset** whose `conditions` match the ref name, and whose enforcement is `active`. 2. Each **organization ruleset** whose conditions match both the repository (by name or repository property) and the ref, again if active. 3. The **classic branch protection rule** whose name pattern matches the branch, if one exists. All of their rules are then applied together. There is no precedence order in the sense of "the most specific one wins and the others are ignored" — that intuition, borrowed from CSS or firewall rules, is the single biggest source of confusion here. Where two sources state the same constraint with different strength, the outcome is the strictest reading: approvals take the highest required count; if any applicable source requires signed commits, signed commits are required; if any blocks force pushes, force pushes are blocked. Nothing can *un-require* something another source requires. ## Enforcement status filters, it does not weaken A ruleset set to `disabled` is simply not in the collection. A ruleset in `evaluate` mode (available on plans that offer it) is evaluated and its would-be violations are recorded in rule insights, but it does not block the operation. This is the mechanism for staging a stricter baseline: publish it in evaluate, watch which repositories and which people trip it, fix them, then flip to active. ## Bypass is per-ruleset, not global Each ruleset carries its own `bypass_actors`. If you are in the bypass list of the organization baseline ruleset, you are exempt from **that ruleset's** rules; a team ruleset that also targets `main` still applies to you in full, and so does classic branch protection, whose exemption is governed by its own "Do not allow bypassing the above settings" toggle. So the real question in an incident is not "am I an admin" but "which of the applicable sources exempts me, and is it the one carrying the rule that is blocking me". Bypasses that do fire are recorded, which is what makes a break-glass path acceptable at all. ## Targeting subtleties that bite - Ref conditions use name patterns plus special targets such as the default branch and all branches, and support both include and exclude lists. An exclude in one ruleset removes only that ruleset from the collection — it does not exclude the branch from anybody else's rules. - Organization rulesets can target repositories by **repository property**, so changing a property value silently changes which policies apply to a repository. Treat property changes as policy changes. - Tag rulesets and push rulesets are separate targets; a branch ruleset never governs tag pushes, so "main is protected" says nothing about whether release tags can be moved. ## How to actually find out Do not reason from screens. Ask GitHub for the effective rules on the branch. The response lists each rule that currently applies with the source it came from — repository or organization, and the identifier of the ruleset — which turns "why can't I merge" into a one-request answer. For classic protection, the branch protection endpoint returns the rule as its own object. Rule insights on the organization or repository shows the history: which rules were evaluated for a given push or merge, what passed, and who bypassed. ## Migration hygiene Running both models permanently is legal and occasionally deliberate, but usually it is an accident of a half-finished migration, and it costs you comprehensibility: two places to look, two mental models, and a stricter-wins interaction nobody remembers. A clean migration is: capture the effective rules, express them as one ruleset, publish it in evaluate mode alongside the classic rule, confirm rule insights shows it would block exactly the same things and nothing more, switch it to active, then delete the classic rule. Deleting the classic rule first is the version of this that produces an unprotected window. ## Interview framing Lead with "all of them apply, strictest wins, nothing relaxes anything". Then add the two details that show real operational exposure: bypass is scoped to the ruleset that grants it, and the way to answer the question in practice is the branch rules endpoint rather than clicking through settings. Mentioning evaluate mode as the safe way to add a source to the stack rounds it out.

  • You can merge in one repository but not another, with the same role in both. Why?
    Because the set of applicable sources differs. An organization ruleset may target one repository by name or property and not the other, or the bypass entry that covers you exists only in one of them. Query the effective rules for each branch and compare which rulesets contributed and which grant you bypass.
  • How would you migrate from classic branch protection to a ruleset without a protection gap?
    Capture the effective rules first, express them as one ruleset, and publish it in evaluate mode while the classic rule is still active. Confirm from rule insights that it would block the same things and nothing more, switch it to active, and only then delete the classic rule. Deleting first is what creates the unprotected window.
  • Does excluding a branch in one ruleset's conditions exempt it from all policy?
    No. An exclude removes only that ruleset from the collection for that ref. Any other ruleset targeting the branch, and any classic branch protection rule matching it, still applies in full. Exclusions are per-source, which is why the effective-rules query is the only reliable answer.

saying these in an interview costs you the question

  • Assumes the most specific pattern wins and others are ignored
  • Thinks a repository ruleset can relax an organization ruleset
  • Believes bypass on one ruleset exempts you from all of them
  • Says classic protection is ignored once a ruleset exists
  • Treats disabled and evaluate enforcement as the same thing

context