Cấu hình Branch Protection Rules trên GitHub và GitLab: Bảo vệ nhánh main khỏi Code Review bị bỏ qua

Git tutorial - IT technology blog
Git tutorial - IT technology blog

2 giờ sáng. Cái notification Slack vừa làm mình giật mình khỏi giường: “Production down. Ai đó vừa push thẳng lên main.”

Không phải lần đầu. Team mình hồi đó 8 người, không ai cố tình phá — nhưng thiếu guardrail thì tai nạn kiểu này xảy ra như cơm bữa. Một câu git push --force origin main trong lúc vội là đủ để overwrite commit của người khác, hoặc deploy thẳng code chưa test lên production. Cái đêm hôm đó mất 3 tiếng mới rollback xong.

Branch Protection Rules là thứ đã cứu team mình khỏi cái vòng lặp đó. Nhưng GitHub và GitLab implement nó khác nhau khá nhiều — chọn sai cấu hình thì đau hơn là không cấu hình gì.

So sánh cách tiếp cận: GitHub vs GitLab

Cả hai platform đều có branch protection, nhưng triết lý thiết kế hoàn toàn khác nhau. GitHub chọn hướng granular — bạn tick từng hành động muốn chặn. GitLab chọn hướng role-based — bạn định nghĩa ai được làm gì.

GitHub Branch Protection Rules

Vào Settings → Branches → Add branch protection rule. Pattern syntax khá linh hoạt: main, release/*, hay v[0-9]* đều chạy được.

Những option quan trọng nhất cần biết:

  • Require a pull request before merging — bắt buộc tạo PR, push thẳng là bị chặn ngay
  • Require approvals — số reviewer tối thiểu, thường là 1 hoặc 2
  • Dismiss stale pull request approvals when new commits are pushed — approval tự reset khi có commit mới. Cái này quan trọng hơn nhiều người nghĩ: thiếu nó, reviewer approve xong developer thêm 5 file mới rồi merge luôn mà không ai hay
  • Require review from Code Owners — chỉ người trong CODEOWNERS file mới có quyền approve
  • Require status checks to pass before merging — CI phải xanh mới được merge
  • Restrict who can push to matching branches — whitelist người/team được push trực tiếp
  • Allow force pushes / Allow deletions — mặc định tắt, đừng bật trừ khi có lý do cụ thể

GitLab Protected Branches

GitLab đặt phần này ở Settings → Repository → Protected branches. Thay vì chặn từng hành động như GitHub, GitLab dùng role — định nghĩa ai được push, ai được merge, ai được force push:

  • Allowed to merge: No one / Developers + Maintainers / Maintainers
  • Allowed to push and merge: No one / Developers + Maintainers / Maintainers
  • Allowed to force push: toggle on/off
  • Code owner approval required: bật khi có file CODEOWNERS

Một điểm hay bị bỏ qua: Merge request approvals trên GitLab nằm ở Settings → Merge requests, tách hoàn toàn khỏi Protected branches. Đây là chỗ gây nhầm lẫn nhiều nhất — người mới config thường chỉ set protected branch rồi tưởng là xong.

Ưu nhược điểm thực tế

GitHub: Rõ ràng, nhưng bẫy ẩn nhiều

Mọi thứ nằm trong một màn hình, dễ nhìn. Nhưng cái bẫy phổ biến nhất: bật Require approvals: 1 mà quên bật Dismiss stale approvals. Reviewer approve, developer thêm 5 file mới, merge thẳng — không ai review đống code vừa thêm vào.

Thêm một điểm trừ nữa: GitHub Free với private repo giới hạn một số option. Cụ thể, Require review from Code Owners cần GitHub Pro hoặc Team plan mới dùng được.

GitLab: Logic hơn cho team lớn

Tách pushmerge ra riêng là điểm mạnh của GitLab — developer có thể merge nhưng không push trực tiếp, hoặc ngược lại, tùy workflow. Với GitLab Free self-hosted, gần như toàn bộ tính năng đều có. Điểm yếu: approval rules rải rác ở 2-3 chỗ khác nhau, dễ bỏ sót khi mới setup.

Chọn cấu hình phù hợp

Sau khi thử cả hai trên team thực tế, đây là gợi ý theo quy mô:

  • Team nhỏ, 2-5 người, dùng GitHub: Require PR + 1 approval + dismiss stale + CI check. Đơn giản, đủ dùng, không overhead.
  • Team vừa, 6-15 người: Thêm CODEOWNERS + require Code Owner review cho folder quan trọng (src/, infra/). GitHub hay GitLab đều ổn.
  • Self-hosted GitLab, team lớn: Role-based phát huy hơn hẳn. Set Maintainer-only merge, Developer không được push trực tiếp.

Team mình hiện tại 8 người dùng GitHub với require 1 approval + dismiss stale + CI check — vậy là đủ. Merge conflict giảm hẳn vì mọi người buộc phải tạo PR, review nhau, thay vì mạnh ai nấy push.

Triển khai từng bước

GitHub: Cấu hình Branch Protection cho main

Vào Settings → Branches → Add branch protection rule, điền:

Branch name pattern: main

Tick các option sau:

✅ Require a pull request before merging
   ✅ Require approvals: 1
   ✅ Dismiss stale pull request approvals when new commits are pushed
   ✅ Require review from Code Owners (nếu có CODEOWNERS)
✅ Require status checks to pass before merging
   ✅ Require branches to be up to date before merging
✅ Do not allow bypassing the above settings
❌ Allow force pushes  (để tắt)
❌ Allow deletions     (để tắt)

Nếu muốn assign reviewer tự động, tạo file .github/CODEOWNERS:

# File: .github/CODEOWNERS

# Mặc định: mọi thứ cần @lead-dev approve
*  @your-org/lead-devs

# Infrastructure: chỉ @ops-team được approve
/infra/  @your-org/ops-team
/docker/ @your-org/ops-team

# Frontend: frontend team tự review
/src/components/ @your-org/frontend-team

Test thử bằng cách push thẳng lên main:

git checkout main
echo "test" >> README.md
git add . && git commit -m "test direct push"
git push origin main
# Expected output:
# remote: error: GH006: Protected branch update failed for refs/heads/main.
# remote: error: At least 1 approving review is required by reviewers with write access.

Bị chặn là đúng. Thành công.

GitLab: Protected Branch + Approval Rules

Bước 1: Vào Settings → Repository → Protected branches:

Branch: main
Allowed to merge: Maintainers
Allowed to push and merge: No one
Allowed to force push: OFF
Code owner approval required: ON (nếu có CODEOWNERS)

Bước 2: Vào Settings → Merge requests → Merge request approvals — phần này tách riêng, đừng bỏ qua:

✅ Prevent approval by author
✅ Prevent approvals by users who add commits
✅ Remove all approvals when commits are added

Approval rules:
  Rule name: "Require 1 approval"
  Approvals required: 1
  Eligible approvers: [chọn group hoặc user cụ thể]

Bước 3: Tạo MR và thử merge khi chưa có approval — GitLab block với thông báo “Approval rules not satisfied”. Nếu thấy cái này là setup đúng rồi.

Xử lý tình huống khẩn cấp (Hotfix)

Sẽ có lúc cần bypass — production đang cháy, không thể chờ review. Đừng tắt protection rules. Có cách khác:

GitHub: Vào Settings → Branches, tạm bật Allow force pushes cho admin, push xong tắt lại ngay trong vòng 10 phút.

GitLab: Đổi Allowed to push từ “No one” sang “Maintainers”, push hotfix, đổi lại.

Quan trọng: log lại mọi lần bypass. Mình dùng Slack channel #git-bypass-log — mỗi lần bypass là một message ghi rõ: ai, lý do, thời điểm, và đã đổi lại chưa. Sau 3 tháng, kênh đó chỉ có 4 message. Đó là dấu hiệu tốt.

Config hay bị bỏ sót

Bốn cái này team mình đều từng bỏ qua ít nhất một lần:

  • “Require branches to be up to date” (GitHub) — bắt rebase trước khi merge, tránh trường hợp PR tạo từ 3 ngày trước, main đã có 20 commit mới, merge vào thì conflict ngầm
  • “Do not allow bypassing the above settings” (GitHub) — ngay cả admin cũng phải qua PR. Nghe overkill nhưng đây là lý do 95% incident xảy ra: admin vội, bypass luôn
  • “Prevent approvals by users who add commits” (GitLab) — chặn kiểu lách luật: thêm 1 commit nhỏ rồi tự approve để unlock merge
  • Require status checks (GitHub) — nếu chưa có CI thì chưa cần bật. Bật mà không có check nào pass thì block merge vĩnh viễn, không rõ lý do

Branch protection không phải thuốc tiên. Nó chặn tai nạn, không chặn được code xấu nếu reviewer không thực sự đọc. Nhưng với team mình, chỉ cần buộc mọi người tạo PR và chờ 1 approval — thói quen communicate qua comment dần hình thành, review chất lượng hơn, và cái incident 2 giờ sáng đó chưa lặp lại lần nào kể từ đó.

Share: