1. Introduction

Pull Requests (PRs) are vital to our collaborative workflow in GitHub. They allow us to propose changes, review code, and ensure everything is in order before merging new features or bug fixes into the main branch.

However, there are times when we need to prevent certain PRs from being merged prematurely. Whether it’s to enforce code quality standards, ensure that all tests pass, or require specific reviews, blocking PR merges can be crucial to maintaining the integrity of our codebase.

In this tutorial, we’ll walk through the steps to block PR merges in GitHub using the platform’s built-in features, such as branch protection rules, status checks, and required reviews. We’ll also discuss how to customize these settings to fit our team’s workflow.

2. Why Block PR Merges?

Before diving into the how-to, let’s quickly discuss why we might want to block PR merges in the first place. Here are a few common scenarios:

  • Enforcing Code Quality: We might want to ensure that all code meets specific standards before it’s merged. This could involve requiring code reviews, passing automated tests, or checking for adherence to style guides
  • Preventing Incomplete Work: Sometimes, PRs are opened before they’re ready to be merged. Blocking merges helps us ensure that work is fully completed and reviewed before being added to the main branch
  • Avoiding Conflicts: By blocking PRs until they’re thoroughly reviewed and tested, we reduce the risk of conflicts and ensure that all changes are compatible with the existing codebase

Blocking PR merges helps us maintain a stable, high-quality codebase, especially in larger teams or open-source projects.

3. Setting Up Branch Protection Rules

Branch protection rules in GitHub allow us to control what happens before a branch can be merged. By setting these up, we can enforce various requirements that must be met before a PR can be merged.

3.1. Accessing Branch Protection Settings

First, we need to open the repository where we want to set up branch protection rules:

GitHub repository

We then click on the “Settings” tab on the top right of the repository page, scroll down to the “Branches” section on the left-hand sidebar, and click on “Branch protection rules”:

branch-protection-rules

3.2. Creating a Branch Protection Rule

We click on the “Add branch ruleset” button to add a rule. Then, under “Targets” we specify the branch we want to protect. We can use wildcards to protect multiple branches or specify the specific branch:

target-branch

We can enable extra options once we indicate which branches we want to protect. Let’s assume we want to enable the “Require pull request reviews before merging” option. This ensures that PRs must be reviewed before they can be merged:

requiered-branch

We can also require the status checks to be passed. To do so, we scroll down to the “Require status checks to pass before merging” section. Here, we can specify that all status checks (for example, CI tests) must pass before merging a PR:

requiered-status-check

In the example above, we enabled “Require branches to be up to date before merging.” This ensures that the merged branch is in sync with the target branch, reducing the risk of conflicts.

Once we configure our rules, we click “Create” or “Save changes” to enforce them.

4. Enforcing Specific Checks

Beyond basic branch protection, we can also enforce specific checks that must pass before a PR can be merged. These might include running tests, linting code, or performing security scans.

4.1. Example: Requiring Tests to Pass

Let’s say we use GitHub Actions for continuous integration (CI). We can set up a CI workflow to run tests automatically and then require those tests to pass before merging.

In our repository’s .github/workflows directory, we create a YAML file that defines our CI workflow. This might include installing dependencies, running tests, and generating reports:

name: CI

on: [push, pull_request]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Set up Node.js
      uses: actions/setup-node@v2
      with:
        node-version: '14'
    - run: npm install
    - run: npm test

When a PR is opened, GitHub Actions will automatically run the workflow, including the tests. If the tests fail, the PR will be blocked from merging. We ensure that our branch protection rules include the status check for this workflow. This way, the PR can’t be merged until all tests pass.

5. Requiring Specific Approvals

Sometimes, we might want to require approval from specific people or teams before a PR can be merged. This can be useful for enforcing code ownership or ensuring that domain experts review changes.

As the article shows, in the branch protection settings, we can enable “Require pull request reviews before merging.” If we have a Code Owners file specified in our repository, we can also require review from code owners before merging:

requiered-reviews

This ensures that the right people review changes to specific parts of the codebase.

6. Handling Exceptions and Overriding Rules

While blocking PR merges is crucial for maintaining code quality, there may be situations where an exception is needed. GitHub allows repository admins to override these rules when necessary, but this power should be used sparingly.

Only users with admin privileges can override branch protection rules. If we’re an admin, we’ll see an option to “Dismiss stale pull request approvals” or “Bypass branch protection rules” when necessary:

dismiss-state

It’s good practice to leave a comment explaining why the override was necessary. This helps us maintain transparency and accountability. One of the ways is also to require the conversation on PR to be resolved before merging:

require-conversation

7. Conclusion

In this tutorial, we discussed blocking PR merges in GitHub, a powerful way to maintain code quality, enforce standards, and prevent incomplete or buggy code from entering the main branch. By setting up branch protection rules, requiring status checks, and enforcing specific reviews, we can ensure that only well-reviewed and fully tested code gets merged.

While these tools are essential for maintaining code integrity, flexibility is also important. Admin overrides and temporary rule adjustments can be useful in exceptional situations, but they should be handled carefully to preserve our codebase’s robustness.

If we frequently need to override rules, it might be worth revisiting our branch protection settings. Perhaps the rules are too strict, or maybe we need to add more flexibility for certain branches.

After making the necessary changes, we must remember to revert the rules to their original state to maintain the protection levels we originally set.