2 giờ sáng, PagerDuty réo chuông liên hồi. Alert báo cổng thanh toán trả về lỗi HTTP 500 hàng loạt. Nguyên nhân: một pull request vừa merge vào nhánh main 5 phút trước đã kích hoạt pipeline CI/CD tự động deploy lên cluster.
Trong cơn ngái ngủ, phản xạ đầu tiên của nhiều người là mở terminal và gõ ngay git revert <commit_hash>. Thế nhưng Git lại từ chối thực thi:
error: commit 9a4f21b is a merge but no -m option was given.
fatal: revert failed
Production đang gián đoạn, mỗi phút downtime gây thiệt hại hàng triệu đồng. Dưới đây là cách dập tắt sự cố trong 30 giây mà không làm xáo trộn git tree của cả team.
Xử lý khẩn cấp trong 3 bước
Nếu server đang gặp sự cố và cần đưa code về trạng thái an toàn ngay lập tức, hãy làm theo quy trình sau:
Bước 1: Lấy mã hash của Merge Commit vừa deploy
Kiểm tra 5 commit gần nhất trên nhánh main:
git log --oneline -n 5
Terminal sẽ trả về log dạng:
9a4f21b (HEAD -> main) Merge pull request #42 from feature/payment-v2
8c1e34a feat: update checkout form
5d2b10f fix: handle payment gateway timeout
1a2b3c4 chore: update dependencies
Bước 2: Chạy lệnh revert với cờ -m 1
Chỉ định rõ nhánh chính (mainline) làm mốc hoàn tác:
git revert -m 1 9a4f21b
Git sẽ tự động mở editor để xác nhận commit message. Bạn chỉ cần giữ nguyên nội dung mặc định Revert "Merge pull request #42 from feature/payment-v2" rồi lưu lại.
Bước 3: Push trực tiếp lên remote để trigger rollback build
git push origin main
Pipeline CI/CD sẽ lập tức build lại bản code sạch trước khi merge. Hệ thống ổn định trở lại. Giờ là lúc bạn có thể thong thả tìm hiểu cơ chế hoạt động thực sự bên dưới của Git.
Bản chất cờ -m và Parent Number trong Git
Commit thông thường chỉ có duy nhất 1 commit cha (parent commit). Khi nhận lệnh git revert <hash>, Git chỉ việc so sánh commit đó với cha của nó để đảo ngược thay đổi.
Merge Commit thì khác. Đây là điểm hội tụ của ít nhất hai nhánh, đồng nghĩa nó sở hữu từ 2 parent commit trở lên:
- Parent 1 (Mainline): Nhánh nhận merge (thường là
mainhoặcproduction). - Parent 2: Nhánh được merge vào (ví dụ:
feature/payment-v2).
Muốn kiểm tra thứ tự các parent này, hãy gõ:
git show 9a4f21b
Quan sát dòng thứ hai trong kết quả trả về:
commit 9a4f21b0e419b48...
Merge: 1a2b3c4 8c1e34a
Author: Senior Dev <[email protected]>
Date: Fri Oct 2 02:00:00 2026
Ý nghĩa các thông số:
1a2b3c4là Parent 1 (HEAD củamainngay trước thời điểm merge).8c1e34alà Parent 2 (commit cuối cùng từ nhánhfeature/payment-v2).
Tham số -m 1 (viết tắt của --mainline 1) đưa ra chỉ thị: “Lấy Parent 1 làm hệ quy chiếu chuẩn và đảo ngược toàn bộ code mà Parent 2 mang vào.”
Hệ quả ít người ngờ tới khi revert merge commit
Nhiều dev lầm tưởng revert sẽ xóa sổ nhánh feature khỏi commit history. Nhưng Git không hoạt động như vậy.
Lịch sử các commit cũ vẫn nằm nguyên trên Git tree. Git chỉ sinh thêm một commit đảo ngược code tại thời điểm revert. Chính điều này sẽ gây rắc rối lớn khi bạn muốn merge lại nhánh feature sau này.
Kỹ thuật Re-merge sau khi đã fix bug
Kịch bản rất phổ biến: Hôm sau, team tìm ra nguyên nhân gây lỗi Null Pointer, sửa xong và đẩy commit mới lên feature/payment-v2. Bạn tự tin mở PR vào main. Thế nhưng khi nhìn tab Files Changed, bạn ngỡ ngàng: Toàn bộ code cũ của feature biến mất, PR chỉ hiển thị đúng vài dòng commit fix mới.
Tại sao lại như vậy? Nhánh main vẫn ghi nhận rằng các commit cũ đã được đưa vào một lần và sau đó bị chủ động loại bỏ. Git hiểu đó là hành động có chủ đích, nên nó sẽ bỏ qua toàn bộ thay đổi cũ.
Quy trình 3 bước để Re-merge an toàn:
Bước 1: Revert lại chính commit revert hôm trước
Ta cần khôi phục lại toàn bộ code cũ trên main bằng cách đảo ngược commit rollback trước đó:
# Đồng bộ nhánh main mới nhất
git checkout main
git pull origin main
# Lấy hash của commit revert (ví dụ: a1b2c3d)
git log --oneline -n 3
# Đảo ngược commit revert (lưu ý: đây là commit đơn nên không dùng cờ -m)
git revert a1b2c3d
Code gốc của feature lúc này đã xuất hiện trở lại trên main.
Bước 2: Merge nhánh feature đã sửa lỗi vào
git merge feature/payment-v2
Git sẽ kết hợp cả phần code cũ vừa phục hồi lẫn các commit sửa lỗi mới nhất.
Bước 3: Xử lý conflict (nếu có) và push lên remote
git add .
git commit -m "fix: resolve conflicts and re-merge feature/payment-v2"
git push origin main
Kinh nghiệm thực chiến khi xử lý sự cố Git
Sau nhiều năm trực chiến hệ thống microservices với hàng chục lượt deploy mỗi ngày, đây là 4 nguyên tắc bạn nên áp dụng:
- Tránh xa
git reset --hardtrên branch dùng chung: Thao tácgit reset --hard HEAD~1kèmgit push --forcesẽ phá vỡ local workspace của các dev khác trong team. Tệ hơn, nó làm mất toàn bộ audit log của pipeline CI/CD. - Ghi rõ ngữ cảnh sự cố trong commit message: Khi revert, nên gắn thêm ticket sự cố vào message để tiện trace log sau này:
git revert -m 1 9a4f21b --no-edit && git commit --amend -m "revert: rollback PR #42 due to checkout timeout (INC-1042)". Lưu ý không truyền trực tiếp cờ-m "message"cùng lúc với-m 1vì trong lệnh revert,-mđược Git mặc định hiểu là--mainline. - Tạo branch trung gian khi re-merge: Thay vì re-merge thẳng trên
main, hãy tạo nhánhre-merge/payment-v2và mở Pull Request. Nhờ đồng đội review lại diff trước khi merge để đảm bảo không sót logic nào. - Chuẩn hóa runbook cho team: Đưa quy trình
git revert -m 1vào tài liệu on-call của dự án. Nhờ vậy, ngay cả fresher hay junior khi trực đêm cũng có thể rollback độc lập trong vòng 2 phút mà không cần đánh thức Tech Lead.

