Skip to content

Git Debugging: Bisect & Blame 🔍

ROLE: HPN (Investigative Engineering).
AUDIENCE: Engineers debugging production issues and investigating code history.

Bugs xuất hiện. Đó là điều chắc chắn. Điều quan trọng là bạn tìm ra chúng nhanh như thế nào. Module này trang bị cho bạn hai vũ khí mạnh nhất để điều tra lịch sử Git: Bisect (Binary Search) và Blame (Accountability).


🎯 Mục tiêu

Sau module này, bạn sẽ:

  • Tìm commit gây bug trong 500 commits chỉ với ~9 bước (O(log N))
  • Tự động hóa bisect với test scripts
  • Dùng git blame để hiểu context thay đổi, không chỉ tìm "ai viết"
  • Master các flags nâng cao để ignore noise

🔬 Git Bisect - Binary Search for Bugs

The Problem

"Tuần trước code còn chạy. Hôm nay bug. Có 500 commits giữa hai thời điểm. Tìm commit nào gây bug?"

Cách thủ công: Check từng commit → O(N) → Mất cả ngày

Cách thông minh: Binary Search → O(log N) → ~9 bước cho 500 commits

log₂(500) ≈ 9 steps

How Bisect Works

Mỗi bước loại bỏ 50% commits. Sau 9-10 bước, bạn tìm được commit gây bug.


Basic Bisect Workflow

bash
# Step 1: Bắt đầu bisect session
git bisect start

# Step 2: Mark commit hiện tại là BAD (có bug)
git bisect bad

# Step 3: Mark commit cũ là GOOD (chưa có bug)
git bisect good v1.0.0  # hoặc commit SHA

# Git checkout commit giữa, bạn test...
# Nếu có bug:
git bisect bad

# Nếu không có bug:
git bisect good

# Lặp lại cho đến khi Git thông báo:
# "abc123def is the first bad commit"

# Step 4: Kết thúc và quay về branch
git bisect reset

Example Session

bash
$ git bisect start
$ git bisect bad HEAD
$ git bisect good v2.0.0

Bisecting: 256 revisions left to test after this (roughly 8 steps)
[abc123] feat: add user authentication

# Test code... có bug!
$ git bisect bad

Bisecting: 128 revisions left to test after this (roughly 7 steps)
[def456] refactor: database connection

# Test code... không bug
$ git bisect good

Bisecting: 64 revisions left to test after this (roughly 6 steps)
...

# Sau ~8 steps:
abc789def is the first bad commit
commit abc789def
Author: Developer <dev@example.com>
Date:   Mon Dec 18 2023

    feat: add caching layer

 src/cache.py | 42 ++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

Automated Bisect - The Power Move

Thay vì test thủ công, viết script và để Git tự chạy:

bash
# Tạo test script
cat > test.sh << 'EOF'
#!/bin/bash
# Exit 0 = GOOD, Exit 1-124 = BAD, Exit 125 = SKIP

# Run your test
npm test 2>&1 | grep -q "PASS"
EOF

chmod +x test.sh

# Chạy bisect tự động
git bisect start
git bisect bad HEAD
git bisect good v1.0.0
git bisect run ./test.sh

# Ngồi uống cà phê, Git tự tìm commit ☕

Exit Codes for bisect run

Exit CodeMeaningGit Action
0GOOD - No bugMark good, continue
1-124BAD - Bug foundMark bad, continue
125SKIP - Can't testSkip this commit
126-127ErrorAbort bisect

Advanced: Skip Untestable Commits

bash
# Nếu commit không build được, skip nó
git bisect skip

# Hoặc trong script:
#!/bin/bash
if ! make build 2>/dev/null; then
    exit 125  # Skip
fi

./run_tests.sh

Real-World Script Examples

Python Project

bash
#!/bin/bash
# test_bisect.sh

# Install dependencies (nếu cần)
pip install -r requirements.txt -q 2>/dev/null || exit 125

# Run specific test
python -m pytest tests/test_auth.py -x -q 2>&1

# pytest exit code: 0 = pass, 1+ = fail

Node.js Project

bash
#!/bin/bash
# test_bisect.sh

npm install --silent 2>/dev/null || exit 125
npm test -- --grep "login feature" 2>&1

Build Verification

bash
#!/bin/bash
# Tìm commit gây build failure

make clean && make build 2>&1
# Exit 0 = builds, Exit 1+ = broken

🎯 Bisect Tips

PRO TIP: Bisect với Terms tùy chỉnh

Thay vì good/bad, dùng terms phù hợp hơn:

bash
git bisect start --term-old=working --term-new=broken
git bisect broken HEAD
git bisect working v1.0.0

CẢNH BÁO: Đừng quên reset!

Luôn chạy git bisect reset khi xong. Nếu không, bạn sẽ ở trạng thái DETACHED HEAD.


📋 Git Blame - Beyond "Who Did This"

The Typical Use

bash
# Ai viết dòng này?
git blame src/auth.py

Output:

a1b2c3d4 (Alice   2023-12-01 10:00:00 +0700  1) def authenticate(user):
e5f6g7h8 (Bob     2023-12-15 14:30:00 +0700  2)     if not user.is_valid():
i9j0k1l2 (Alice   2023-12-01 10:00:00 +0700  3)         return False
m3n4o5p6 (Charlie 2023-12-20 09:15:00 +0700  4)     return check_password(user)

Beyond "Who" - Understanding "Why"

Blame không chỉ để tìm người đổ lỗi. Nó giúp hiểu context của thay đổi.

bash
# Xem commit message của từng dòng
git blame -c src/auth.py

# Xem full commit info
git blame -l src/auth.py

# Show commit SHA đầy đủ
git log -1 --format="%B" <sha-from-blame>

Blame Specific Lines

bash
# Chỉ blame dòng 10-20
git blame -L 10,20 src/auth.py

# Blame từ dòng 10 đến hết file
git blame -L 10, src/auth.py

# Blame function cụ thể (regex)
git blame -L '/def authenticate/,/^def /' src/auth.py

🔇 Ignoring Noise

Problem: Whitespace/Formatting Changes

Ai đó chạy prettier và format lại toàn bộ file. Giờ blame hiển thị họ là "author" của mọi dòng.

bash
# Ignore whitespace changes
git blame -w src/auth.py

# Ignore moved/copied lines trong cùng file
git blame -M src/auth.py

# Ignore moved/copied từ file khác
git blame -C src/auth.py

# Combo: ignore tất cả noise
git blame -w -M -C src/auth.py

Using .git-blame-ignore-revs

Tốt hơn: Tạo file list các commits cần ignore (formatting, mass refactors):

bash
# Tạo file .git-blame-ignore-revs
cat > .git-blame-ignore-revs << EOF
# Prettier formatting
abc123def456789

# ESLint auto-fix
def789ghi012345
EOF

# Dùng file này khi blame
git blame --ignore-revs-file .git-blame-ignore-revs src/auth.py

# Hoặc config globally cho repo
git config blame.ignoreRevsFile .git-blame-ignore-revs

TEAM BEST PRACTICE

Commit file .git-blame-ignore-revs vào repo. Mọi người trong team sẽ có blame sạch.


Blame Through Renames

bash
# Theo dõi file qua renames
git blame --follow old_name.py

Show Original Commit (Pre-move)

bash
# Hiển thị commit gốc, không phải commit move code
git blame -C -C -C src/auth.py

Ba -C flags:

  1. -C: Detect moves/copies trong commit này
  2. -C -C: ...và trong commit tạo file
  3. -C -C -C: ...và trong bất kỳ commit nào

🔗 Combining Bisect + Blame

Workflow thực tế:

bash
# 1. Dùng bisect tìm commit gây bug
git bisect start
git bisect bad HEAD
git bisect good v1.0.0
git bisect run ./test.sh
# Found: abc123

# 2. Xem commit đó thay đổi gì
git show abc123

# 3. Blame dòng cụ thể để hiểu context
git blame -L 42,50 src/cache.py

# 4. Xem history của dòng đó
git log -p -L 42,50:src/cache.py

📊 Quick Reference

Bisect Commands

CommandPurpose
git bisect startBắt đầu session
git bisect bad [ref]Mark commit có bug
git bisect good [ref]Mark commit không bug
git bisect skipSkip commit không test được
git bisect run <script>Auto-bisect với script
git bisect logXem log session
git bisect resetKết thúc, về branch gốc

Blame Commands

CommandPurpose
git blame <file>Basic blame
git blame -L 10,20 <file>Blame specific lines
git blame -w <file>Ignore whitespace
git blame -M <file>Detect moved lines
git blame -C <file>Detect copied lines
git blame --ignore-revs-file <file>Ignore listed commits

💡 Key Takeaways

HPN'S INSIGHT

"Bisect + automated tests = Debugging superpower. Bạn có thể tìm bug trong 10,000 commits trong vài phút."

  1. Bisect là O(log N): 500 commits = ~9 bước, không phải 500
  2. Automate bisect: Viết test script, để Git chạy
  3. Blame không phải để đổ lỗi: Hiểu context, không phải tìm người
  4. Ignore noise: Use -w -M -C.git-blame-ignore-revs
  5. Combine tools: Bisect tìm commit → Blame hiểu context

SENIOR ENGINEER PERSPECTIVE

Junior devs scroll qua history thủ công. Senior devs dùng bisect run. Khác biệt? Một người mất 4 giờ, một người mất 5 phút.