Migrating GitHub branch protection to rulesets in bulk: Verifying the behavior of Import branch protection rules

Migrating GitHub branch protection to rulesets in bulk: Verifying the behavior of Import branch protection rules

Tested Import branch protection rules to convert rules to rulesets, checking why 1 rule splits into 2, Evaluate handling on Free plan, keeping original rules, and conversation resolution conversion.
2026.10.21

This page has been translated by machine translation. View original

Hi there! I'm Yuji Nishimura from the Operations team!

The feature to convert GitHub branch protection rules to repository rulesets was expanded to support bulk conversion on 2026-10-20. The 2026-08-11 changelog had introduced Convert to ruleset, which converted one rule at a time. This time, Import branch protection rules is now available, allowing you to import all branch protection rules in a repository at once.

For repositories with many rules, bulk import is faster than converting one by one. On the other hand, since multiple rulesets are created with a single button press, it's worth understanding in advance how the rulesets will be divided and what happens to the original rules. In this article, I run the bulk import on a public repository for testing purposes and verify the contents of the generated rulesets, plan-based limitations, how the original rules are handled, and the conversion results for conversation resolution.

What Changed This Time

  • Target / Date: All repositories where rulesets are available (announced 2026-10-20)
  • Key changes:
    • Branch protection rules in a repository can be bulk-converted to rulesets via Import branch protection rules in Settings → Rulesets
    • The confirmation dialog (Review import) lets you preview the rulesets to be created, rules already covered by existing rulesets, and cases where one rule is split into multiple rulesets
    • You can choose the enforcement status (Active / Evaluate / Disabled) and which rulesets to create
    • After creation, you can choose to keep or delete the original branch protection rules
  • Official link: Bulk migrate branch protections to rulesets for a repository

As of 2026-10-21, the GitHub Docs conversion page still only describes the one-at-a-time Convert to ruleset approach, and the bulk import procedure is only documented in the changelog.

Why Migrate to Rulesets

The GitHub Docs and changelog list the following advantages of rulesets:

  • Pattern-based application to multiple branches: A single ruleset can be applied to multiple branches using pattern matching
  • Layered rules: Multiple rulesets can be applied to the same branch simultaneously, allowing policies to be composed in layers
  • Organization / Enterprise-level management: The same ruleset can be applied across multiple repositories
  • Granular bypass permissions: You can specify who can bypass rules per user, team, or app
  • Visibility for read access: People with read access can view active rulesets, allowing developers to see the rules that apply to them and auditors to check protection status without admin permissions

Trying the Bulk Import

Test Environment

  • A Free plan account on github.com
  • A public repository for testing (deleted after testing)
  • Conducted on 2026-10-21

Since rulesets are not available for private repositories on the Free plan, I tested with a public repository.

Before the conversion, I set up the following three branch protection rules and an existing ruleset targeting develop.

Pattern Branch protection settings
main Require 1 approval, require status check test (also require branches to be up to date before merging), require conversation resolution
release/* Require linear history
develop Require linear history. Already covered by the existing ruleset develop-linear-history, which includes linear history, no force pushes, and no deletions

In all cases, "Do not allow bypassing the above settings" (enforce for administrators) is left disabled. To verify how conversation resolution is handled, I also added a rule for hotfix/* later and imported it (described below).

Checking the Generated Results in Review Import

When I opened Settings → Rulesets in a repository with branch protection rules, a banner prompting conversion and the Import branch protection rules button were displayed.

Banner showing Import branch protection rules on the Rulesets screen

Clicking the button opens the Review import dialog.

Review import dialog showing main and release/* each split into 2 rulesets, and develop shown as already covered by an existing ruleset

The following can be read from the dialog:

  • The heading reads 4 rulesets will be created from 2 branch protection rules., meaning 4 rulesets will be created from the 2 rules for main and release/*
  • main and release/* are each split into 2 rulesets suffixed with -1 and -2. Ruleset names can be changed in the input fields
  • develop has its checkbox disabled and shows Already covered by an existing ruleset. Rules already covered by an existing ruleset are excluded from the import
  • Notes appear for main saying main is not covered / Rulesets do not strictly require status checks / No ruleset contains all required status checks, and for release/* saying No coverage. Coverage status by existing rulesets and settings that don't fully match branch protection behavior are shown before creation

Why One Rule Is Split Into Two Rulesets

When I checked the contents of the created rulesets using gh api repos/<owner>/<repo>/rulesets/<id>, they were divided as follows:

Ruleset Bypass Included rules
main-1 None Restrict deletions, restrict force pushes
main-2 Repository admin Required status checks (strict), pull request (1 approval, require conversation resolution)
release/*-1 None Restrict deletions, restrict force pushes
release/*-2 Repository admin Require linear history

They are split based on whether a bypass is present. About protected branches explains that branch protection restrictions do not apply to people with admin permissions by default. In this case, the rules for approvals and status checks were set not to apply to administrators.

On the other hand, restrictions on deletions and force pushes were placed in the ruleset with no bypass. From this result, it appears that the conversion treats deletion and force push restrictions as rules that apply to administrators as well, and separates them into a different ruleset from the parts that administrators can bypass. Since bypass is configured at the ruleset level in rulesets, rules with different bypass handling cannot be combined into one.

The changelog notes that one branch protection rule may result in multiple rulesets being created to preserve the original behavior. The split in this case is an instance of that. Since ruleset names can be changed during import, using names that convey their purpose—such as main-protect-all and main-admin-bypass—will make the list easier to understand later.

Evaluate Appears in the UI But Cannot Be Created on the Free Plan

The Enforcement status dropdown showed all three options—Active, Evaluate, and Disabled—even on the Free plan. The default is Active. Note that Evaluate is a mode that does not enforce rules but only records violations in Rule Insights.

When I selected Evaluate and clicked Create rulesets, the following error appeared and no ruleset was created:

Selecting Evaluate and attempting to create shows a plan-incompatibility error

Enforcement evaluate option is not supported on this plan. Please upgrade to Enterprise to enable it.

Attempting to create a ruleset with enforcement: "evaluate" via the REST API also returned a 422 error with the same message. GitHub Docs also only shows the Evaluate description on pages intended for GitHub Enterprise Cloud / Server. I only tested on the Free plan, but based on the error message and Docs, Evaluate appears to be an Enterprise-only feature. If Evaluate is unavailable, you would create the ruleset as Active, keep the original rules, and run both in parallel.

Whether to Keep or Delete the Original Branch Protection Rules

When I switched back to Active and clicked Create rulesets, a dialog appeared asking whether to delete the original rules.

Dialog after creating rulesets asking whether to keep or delete the original branch protection rules

Selecting Keep rules added four rulesets in Active status.

Ruleset list after bulk import, showing main-1, main-2, release/*-1, and release/*-2 added

When I navigated to the Branches screen, all the original branch protection rules remained, and each showed the message This rule is fully covered by rulesets and can be safely deleted. along with a Delete button.

Branches screen showing all three branch protection rules marked as covered by rulesets

According to GitHub Docs, if the original rules are kept, both the branch protection and the Active ruleset are applied simultaneously, and changes must satisfy both. Keeping them ensures there is no gap in protection immediately after creating the rulesets. You can verify coverage on the Branches screen and then delete the original rules using Delete.

When importing the hotfix/* rule described below, I chose to delete. When there is only one target, the button appears in singular form as Delete branch protection rule, and the original rule disappears from the branch protection rule list at the same time the ruleset is created.

Conversation Resolution Is Converted as Part of the Pull Request Rule

GitHub Docs states that the conversion covers all branch protection types except Require conversation resolution before merging. The reason for the exclusion is that in rulesets, conversation resolution is part of the pull request rule and only applies when the pull request rule is enabled, so there is no one-to-one correspondence.

However, when I actually tried the bulk import, conversation resolution was also converted.

  • For main, which used it together with required approvals, it was carried over to the pull request rule in main-2 as required_review_thread_resolution: true
  • After the first import, I added hotfix/* with only conversation resolution required and imported it. It was split into two rulesets: hotfix/*-1 (no bypass, restrict deletions and force pushes) and hotfix/*-2 (Repository admin bypass). hotfix/*-2 contained a pull request rule with 0 required approvals (required_approving_review_count: 0, required_review_thread_resolution: true)

When importing hotfix/*, the following note appeared in Review import. The three rules converted earlier now show as Already covered by an existing ruleset at this point.

When importing a rule with only conversation resolution required, a note appears stating that pull requests will also be required in the ruleset

According to the note, requiring conversation resolution in a ruleset also requires a pull request, meaning it can no longer be bypassed by merging without going through a pull request. I didn't test actual pushes this time, but for branches that only required conversation resolution, it seems that after conversion, changes can only be merged via a pull request—except for administrators who are listed as bypass. When migrating branches that allowed direct pushes, this difference should be confirmed in advance.

Converting One Rule at a Time (Convert to ruleset)

The one-at-a-time conversion method added on 2026-08-11 is still available. Since I only tested the bulk import this time, this section is based on what GitHub Docs describes.

  1. Open the repository's Settings → Branches
  2. Find the target rule in the Branch protection rules list and click Convert to ruleset
  3. Set the ruleset name and review the changes in the New behavior section
  4. Optionally select Delete branch protection rule once migration is done, then click Create ruleset (or Create 2 rulesets if multiple will be created)

The Docs for non-Enterprise plans state that rulesets created this way are always Active. The Docs procedure does not include a step to review all rules already covered by existing rulesets at once. You can use this method to migrate specific rules first, and use bulk import to migrate an entire repository.

Summary

GitHub branch protection rules can now be bulk-converted to rulesets via Import branch protection rules in Settings → Rulesets as of 2026-10-20. The Review import screen lets you confirm the rulesets to be created and the rules already covered by existing rulesets, and you can decide whether to keep or delete the original rules—all in one flow.

When I tested on a public repository on the Free plan, one rule was split into two rulesets based on whether the administrator bypass was present. Although Evaluate appears in the dropdown, it cannot be created on the Free plan, and the error prompts an upgrade to Enterprise. Conversation resolution is documented as outside the scope of conversion, but in practice it is carried over as part of the pull request rule, making pull requests mandatory.

When migrating, it's safer to keep the original rules temporarily, verify coverage on the Branches screen, and then delete them to avoid any gap in protection. If you want to migrate only specific rules, the conventional Convert to ruleset is also available.

I hope this is helpful to someone.


Reference links:


そのCCoE、つくって終わりになっていませんか

ガバナンスのルールも組織ポリシーも、整えた直後はちゃんと機能する。でも運用が属人化すれば、CCoEはいつのまにか形だけに。ベンダー依存から内製へ踏み出すために、中の人として実行まで伴走する。立ち上げと定着の進め方を、無料資料にまとめました。

CCoE総合支援

CCoE立ち上げ資料をダウンロード

Share this article