Branch Protection Rules
Main pe direct push? BLOCKED. Club ka bouncer — No ID, no entry!
Branch protection rules are the bouncer at the door of your most important branches. Without them, anyone with push access can do whatever they want to main, develop, or release/* branches.
Without protection, any developer can:
- Push directly to main — no review, no CI, just raw code going live
- Force push to main — rewrite history, destroy commits
- Delete the main branch entirely
With branch protection enabled:
- Must use a Pull Request — no direct pushes allowed
- Must get code review approval from teammates
- CI must pass — tests green before merging
- No force push — history is safe
- No deletion — the branch cannot be removed
Configured in GitHub Settings > Branches > Branch protection rules. Protect these branches: main, develop, release/* — any branch that should not be modified directly.
GitLab calls them "Protected Branches", Bitbucket calls them "Branch Permissions". Same concept, different names.
# Without protection (DANGEROUS):
git push origin main # anyone can push directly
git push --force origin main # anyone can rewrite history
git push origin --delete main # anyone can delete
# With protection (SAFE):
git push origin main
# ERROR: protected branch — must use PR
The 6 essential branch protection rules every team should enable on main:
- 1. Require pull request before merging — no direct pushes to the branch. All changes must come through a PR. This is the most fundamental rule.
- 2. Require approvals — 1 or more reviewers must approve the PR before it can be merged. Two pairs of eyes catch bugs that one misses.
- 3. Require status checks to pass — CI pipeline (build, test, lint) must be green. No merging broken code.
- 4. Require signed commits — commits must be signed with GPG key. Verifies that the person who committed is actually who they claim to be.
- 5. Require linear history — no merge commits allowed. All PRs must be rebased before merging. Clean, readable history.
- 6. Disable force pushes — no
git push --forceon the branch. History cannot be rewritten.
Additional options: restrict who can push to specific teams, require review from Code Owners, dismiss stale reviews when new commits are pushed.
# GitHub Protection Settings:
# 1. Require a pull request before merging
# - Require approvals (1-6)
# - Dismiss stale reviews on push
# - Require review from Code Owners
#
# 2. Require status checks to pass
# - CI checks: build, test, lint
# - Require branches to be up to date
#
# 3. Require signed commits (GPG)
#
# 4. Require linear history (no merge commits)
#
# 5. Include administrators (or not)
#
# 6. Allow force pushes (usually OFF)
# Allow deletions (usually OFF)
Setting up branch protection in the GitHub web interface:
- Step 1: Navigate to your repository on GitHub
- Step 2: Click Settings tab (you need admin access)
- Step 3: Click Branches in the left sidebar
- Step 4: Click Add rule under Branch protection rules
- Step 5: Enter the branch name pattern:
main - Step 6: Check the protection rules you want
- Step 7: Click Create
Branch name patterns support wildcards: main (exact match), release/* (all release branches), feature/** (all feature branches).
"Include administrators" — when checked, even repository admins must follow the same protection rules. This is critical for consistent enforcement.
For organizations: use branch protection templates to apply the same rules across multiple repositories consistently.
# GitHub UI Setup:
# 1. Go to repo Settings
# 2. Click "Branches" in sidebar
# 3. Click "Add rule"
# 4. Branch name pattern: main
# 5. Check desired protections
# 6. Save changes
# Common setup for main:
# ✅ Require a pull request before merging
# ✅ Require approvals: 1
# ✅ Require status checks: ci/lint, ci/test
# ✅ Require linear history
# ❌ Allow force pushes
# ❌ Allow deletions
# ✅ Include administrators
Using the GitHub CLI (gh) to set branch protection programmatically is essential for:
- Infrastructure as code — protection rules defined in scripts, not just UI clicks
- Team onboarding — new repos get protection automatically
- Consistency — same rules across all repositories in an organization
- Audit trail — changes to protection are tracked in scripts
The GitHub REST API endpoint for branch protection: PUT /repos/:owner/:repo/branches/:branch/protection
Using gh api you can create, read, update, and delete protection rules from the terminal.
# Install GitHub CLI
gh auth login
# Set branch protection via API
gh api repos/:owner/:repo/branches/main/protection \
--method PUT \
--field required_status_checks='{"strict":true,"contexts":["ci/test","ci/lint"]}' \
--field enforce_admins=true \
--field required_pull_request_reviews='{"required_approving_review_count":2}' \
--field restrictions=null
# View current protection
gh api repos/:owner/:repo/branches/main/protection
# Delete protection
gh api repos/:owner/:repo/branches/main/protection \
--method DELETE
Even with branch protection enabled, teams make these common mistakes:
- Enabling protection without telling the team — developers suddenly can't push and don't know why. Communicate BEFORE enabling rules.
- Setting approval count too high (5+) — PRs wait weeks for enough approvals. 1-2 is usually enough for most teams.
- Status check name typo — CI job is named
testbut protection requirestests. PRs are blocked forever because the check never passes. - Not including administrators — admins bypass all rules, making protection meaningless.
- Forgetting CODEOWNERS — reviews assigned randomly instead of to domain experts who understand the code.
- Protection is not a substitute for code review culture — forcing approval is not the same as thoughtful review.
# MISTAKE 1: Wrong status check name
# CI job is named "test" but protection says "tests"
# PR blocked forever!
# MISTAKE 2: Too many required approvals
# 5 approvals = PR waits weeks
# MISTAKE 3: Not including admins
# Admin can push directly to main, bypassing all rules
# Always enable "Include administrators"
# MISTAKE 4: No CODEOWNERS file
# Reviews assigned randomly, not to domain experts
# Create CODEOWNERS
cat > .github/CODEOWNERS << 'EOF'
# Default reviewers
* @team-leads
# Payment module must be reviewed by payment team
/src/payment/ @payment-team
# Auth must be reviewed by security
/src/auth/ @security-team
EOF
Lo kar liya — Key Points:
- ✅ Branch protection rules prevent dangerous operations on important branches (main, develop, release)
- ✅ 6 essential rules: require PR, require approvals, require CI, require signed commits, linear history, no force push
- ✅ Set up in GitHub: Settings > Branches > Add rule — enter branch name pattern and check desired protections
- ✅ Use GitHub CLI (
gh api) for programmatic setup and consistency across repos - ✅ Always enable "Include administrators" — no one should bypass protection, not even admins
- ✅ Status check names must match CI job names exactly — a single typo blocks all PRs permanently
Want to track your progress?
Log in to save your place and pick up where you left off.
Progress track karna chahte ho?
Login karo apni progress save karne ke liye aur jahan chhoda tha wahan se shuru karo.
Login