GitLab branches are your project’s history, and protecting them is like putting a lock on your most important documents.

Let’s say you’re working on a critical feature branch, feature/new-dashboard, and you want to make sure no one accidentally overwrites it or merges unfinished code into main. This is where branch protection rules come in. They’re a set of policies you define within GitLab to control who can push to a branch, who can merge into it, and what checks must pass before a merge can happen.

Imagine your team is pushing code. Without protection, a developer could accidentally force-push over main, wiping out recent commits. Or, a junior developer might merge a half-baked feature directly into the release branch, causing production issues. Branch protection prevents these scenarios by enforcing a workflow.

Here’s how it works in practice. You navigate to your GitLab project’s Settings > Repository. Under the "Branch rules" section, you can add new rules.

Let’s protect the main branch.

Rule 1: Prevent Force Pushes

  • What it protects: This prevents anyone from using git push --force or git push --force-with-lease on the protected branch. Force pushing rewrites history, which can be disastrous if others have based their work on the original history.
  • Configuration:
    • Branch name: main
    • Allowed to merge: Select roles or users who can merge into this branch. For main, you’d typically restrict this to specific roles like "Maintainers" or a dedicated "Release Team."
    • Allowed to push: Select roles or users who can push directly to this branch. For main, you must deny all pushes here to prevent direct commits.
    • Prevent force push: Check this box.
  • Why it works: GitLab intercepts any git push --force attempt targeting main and rejects it. This ensures that the branch’s history remains linear and predictable, preventing accidental data loss or confusion.

Rule 2: Require Code Review Before Merging

  • What it protects: This ensures that no code can be merged into the protected branch without at least one approval from a designated reviewer.
  • Configuration:
    • Branch name: main
    • Allowed to merge: (As above, restricted)
    • Allowed to push: (Denied)
    • Require approval from code owners: Enable this if you have a CODEOWNERS file to automatically assign reviewers based on file paths.
    • Require approvals: Set this to 1 or more. This means at least one person must approve the merge request.
    • Require status checks to pass: Check this box. This is crucial. It means all CI/CD pipelines (tests, linters, etc.) must succeed before merging.
  • Why it works: When a developer creates a merge request targeting main, GitLab will block the merge button until the specified number of approvals are given and all configured status checks (like passing unit tests) are green. This guarantees that code is reviewed and validated before entering the main branch.

Rule 3: Protecting Specific Branches (e.g., release/*)

  • What it protects: You can use wildcards to protect multiple branches that follow a pattern. For example, all branches starting with release/.
  • Configuration:
    • Branch name: release/*
    • Allowed to merge: Typically "Maintainers" or "Developers."
    • Allowed to push: Deny all pushes to prevent direct commits.
    • Prevent force push: Check this box.
    • Require approvals: Set to 1 or more.
    • Require status checks to pass: Check this box.
  • Why it works: This applies the same safety net to all your release branches, ensuring consistency and preventing accidental overwrites or unreviewed code from being tagged for release.

Rule 4: Allowing Specific Users/Groups to Bypass Rules

  • What it protects: In rare cases, you might need a specific user or group (e.g., a DevOps administrator) to bypass certain rules, like force pushing to a specific branch for emergency hotfixes.
  • Configuration: Within the branch rule settings, there’s usually an "Allowed to push" or "Allowed to merge" section where you can add specific users or groups. You can also configure "Protected branches" to have a "No one" setting for "Allowed to push," and then explicitly add specific users or groups to the "Allowed to push" list for that rule.
  • Why it works: This acts as an override mechanism. GitLab checks if the acting user is in an allowed list before enforcing the "prevent force push" or "deny all pushes" restrictions. Use this sparingly.

Rule 5: Default Branch Protection

  • What it protects: GitLab often has a default branch (usually main or master). You can set up default protection rules that apply to any new branches created in the project.
  • Configuration: In Settings > Repository > Default branch, you can configure default branch protection. This might involve setting default requirements for reviews or status checks.
  • Why it works: This ensures that all newly created branches automatically inherit a baseline level of protection, reducing the chance of human error when setting up new workflows.

The one thing most people don’t know: You can configure branch rules to only allow developers to push to their own feature branches (e.g., feature/*), while still preventing direct pushes and force pushes to main and develop. This is done by creating separate rules: one for main (deny all pushes, prevent force push) and another for feature/* (allow developers to push, but maybe still prevent force push). This granular control is powerful for maintaining clean histories across different development streams.

Once you’ve set up these rules, the next time someone tries to violate them, they’ll see an error message from GitLab indicating that the action is not allowed due to branch protection. You’ll then need to address the reason for the violation, whether it’s a misunderstanding of the workflow or a genuine need to adjust the rules.

Want structured learning?

Take the full Gitlab course →