Khi production báo lỗi nhưng không biết commit nào gây ra
Sprint vừa xong, deploy lên production, 30 phút sau Slack nổ tưng bừng: “Bug rồi, cái feature A bị lỗi!” Bạn chạy git log… 47 commit trong sprint. Bắt đầu review từng cái? Mất cả buổi chiều.
Tình huống này xảy ra khá thường xuyên — dự án microservices 4 người, cứ mỗi sprint lại tích lũy vài chục commit mới. Phản xạ đầu tiên lúc đó là git checkout về từng commit rồi test tay. Rồi mình nhớ lại git bisect — thứ đã đọc qua nhưng chưa bao giờ thực sự mó tay vào.
Sau lần đầu dùng bisect tìm ra bug trong 5 phút thay vì 3 tiếng, mình không bao giờ quay lại cách cũ nữa.
Git bisect hoạt động thế nào
Ý tưởng đơn giản: thay vì dò từng commit theo thứ tự, git bisect chia đôi lịch sử commit và hỏi: “Commit này có bug không?” Bạn trả lời “có” hoặc “không”, rồi nó tiếp tục chia đôi phần còn lại. Đây chính xác là binary search.
Nếu có 1000 commit, cách thủ công tệ nhất là kiểm tra 1000 lần. Với bisect, tệ nhất chỉ cần log₂(1000) ≈ 10 lần. Với 47 commit? Khoảng 6 lần kiểm tra là xong.
Toàn bộ logic xoay quanh hai nhãn đơn giản:
- bad: commit có bug (thường là HEAD hiện tại)
- good: commit không có bug (một commit cũ bạn biết chắc là ổn)
Git bisect tự động checkout về commit ở giữa, bạn test rồi đánh dấu bad hoặc good — cứ thế đến khi thu hẹp được thủ phạm.
Thực hành từng bước với ví dụ thực tế
Bắt đầu phiên bisect
Giả sử bạn đang ở nhánh main, HEAD là commit mới nhất và có bug:
# Bắt đầu
git bisect start
# Đánh dấu commit hiện tại là bad (có bug)
git bisect bad
# Đánh dấu một commit cũ là good — dùng tag release hoặc hash cụ thể
git bisect good v1.2.0
# hoặc dùng hash
git bisect good abc1234
Git lập tức checkout về commit giữa khoảng và cho bạn biết còn bao nhiêu bước:
Bisecting: 23 revisions left to test after this (roughly 5 steps)
[f3d9a2b] Fix login validation
Vòng lặp test → đánh dấu
Ở mỗi commit git dẫn bạn đến, chạy test hoặc reproduce bug thủ công, rồi đánh dấu:
# Nếu commit này CÓ bug
git bisect bad
# Nếu commit này KHÔNG có bug
git bisect good
Lặp lại đến khi git thông báo tìm ra:
e4f5c6d is the first bad commit
commit e4f5c6d
Author: Nguyen Van A <[email protected]>
Date: Mon Feb 10 14:32:11 2026 +0900
Refactor user authentication module
:040000 040000 abc... def... M src/auth
Đó rồi. Commit e4f5c6d là thủ phạm.
Kết thúc và quay lại HEAD
git bisect reset
Lệnh này đưa bạn về lại HEAD ban đầu. Nhớ chạy cái này sau khi xong — không thì bạn đang ở một commit nào đó giữa lịch sử, dễ nhầm lắm.
Tự động hóa với script
Đây là phần mình thích nhất. Thay vì ngồi test tay từng commit, bạn viết script tự động và để git bisect chạy luôn:
git bisect start
git bisect bad HEAD
git bisect good v1.2.0
# Chạy script tự động — exit code 0 = good, non-zero = bad
git bisect run ./test_bug.sh
File test_bug.sh đơn giản như:
#!/bin/bash
# test_bug.sh — kiểm tra xem feature login có hoạt động không
cd /path/to/project
python -m pytest tests/test_auth.py::test_login -x -q
# exit code tự động từ pytest: 0 = pass, 1 = fail
Hoặc với Node.js:
#!/bin/bash
npm run build 2>/dev/null && node -e "
const auth = require('./dist/auth');
const result = auth.validateUser('[email protected]', 'password');
process.exit(result ? 0 : 1);
"
Git bisect run sẽ tự động chạy script này trên mỗi commit và tự đánh dấu bad/good dựa vào exit code. Bạn chỉ cần ngồi nhìn.
Xem log quá trình bisect
Muốn xem lại đã đi qua những commit nào:
git bisect log
Nếu cần gửi bug report kèm quá trình điều tra, đây là thứ nên đính kèm — team lead sẽ không cần hỏi thêm.
Bỏ qua commit không test được
Đôi khi gặp commit không build được — dependency cũ, config thiếu, hay conflict chưa giải quyết. Lúc đó dùng:
git bisect skip
Git tự nhảy qua, thử commit kế bên.
Một số lưu ý từ thực tế
Xác định “good commit” chính xác: Mình hay dùng git tag để đánh dấu các release. Không có tag? Estimate kiểu: “2 tuần trước feature này còn ổn”. Sau đó chạy git log --oneline --since="2 weeks ago" để lọc hash phù hợp.
Unit test là đòn bẩy của bisect: Project có test coverage tốt thì viết script bisect run chạy đúng test case liên quan là nhanh nhất. Mình từng tìm ra bug chỉ trong 8 phút nhờ cách này — chạy tự động hoàn toàn, không cần ngồi chờ.
Cẩn thận với merge commit: Đôi khi bisect dừng ở một merge commit, cần xem lại cả hai nhánh gốc để hiểu đúng ngữ cảnh thay vì vội kết luận.
Nói đến chuyện cẩn thận — mình từng mất code quan trọng vì force push nhầm branch, từ đó luôn để ý kỹ hơn với mọi thao tác git. Bisect cũng vậy: sau khi tìm ra commit bad, đừng vội git revert ngay mà đọc kỹ diff trước. Nhiều khi commit đó sửa nhiều thứ cùng lúc, revert toàn bộ lại gây vấn đề khác hoàn toàn.
Workflow thực tế khi nhận bug report
Sau nhiều lần debug với bisect, mình đúc ra được một flow cố định:
- Xác nhận bug còn tồn tại ở HEAD — chạy
git statusđể chắc đang ở đúng branch - Tìm “last known good” — thường là tag release gần nhất hoặc commit trước sprint hiện tại
- Viết test case tối giản reproduce bug nếu có thể (để dùng cho
bisect run) - Chạy
git bisect start+git bisect runnếu có script, hoặc đánh dấu tay nếu không - Đọc kỹ diff của commit bad bằng
git show <hash>trước khi fix git bisect resetvề HEAD rồi mới bắt đầu viết fix
Kết luận
Hầu hết dev đều biết git bisect tồn tại — nhưng hỏi “lần cuối dùng là bao giờ?” thì im lặng. Đó là lãng phí.
Sau 6 tháng dùng thực tế, thấy bisect phát huy nhất trong hai tình huống. Một là regression bug sau sprint dài nhiều commit. Hai là codebase lớn mà bạn không có đủ context để biết nên đọc code từ đâu.
Không cần nhớ syntax phức tạp — chỉ cần nhớ: bisect start → đánh dấu bad/good → test từng commit → bisect reset. Lần sau gặp bug mà không biết commit nào gây ra, thử dùng bisect thay vì ngồi lần mò git log thủ công xem sao.

