Bài 03
Khi rối: reflog, reset, bisect, hotfix production
Bài 03 — Khi rối: reflog, reset, bisect, hotfix production
Lỡ delete branch chưa push. Reset hard nhầm. Bug xuất hiện 2 tuần trước, không biết commit nào gây ra. 11h đêm production sập, cần hotfix gấp. Đây là khi bạn cần bài này — Git có sẵn vũ khí cho từng case.
Reflog — vũ khí cứu commit "mất"
Reflog ghi mọi lần HEAD đổi (checkout, commit, reset, rebase). Local only — không sync remote. Giữ ~30 ngày.
git reflogOutput:
8a1b2c3 HEAD@{0}: checkout: moving from feat/x to main
9d4e5f6 HEAD@{1}: commit: Add feature X
c5d6e7f HEAD@{2}: rebase finished
...Mỗi dòng = 1 trạng thái HEAD trong quá khứ.
Lỡ xoá branch chưa push
# Xoá branch, panic
git branch -D feat/x
# Cứu — tìm SHA cuối của branch trong reflog
git reflog | grep feat/x
# 9d4e5f6 HEAD@{1}: commit: Add feature X (last commit on feat/x)
# Tạo lại branch từ SHA đó
git checkout -b feat/x 9d4e5f6Mọi case "mất commit" đều check reflog đầu tiên. 95% case recovery được.
Reset — undo local
3 mode, hiểu mới dùng đúng:
git reset --soft HEAD~1 # undo commit, GIỮ staged + working
git reset HEAD~1 # undo commit, GIỮ working (--mixed mặc định)
git reset --hard HEAD~1 # undo commit + XOÁ thay đổi (nguy hiểm)Hình dung:
Trước reset:
Working: edited
Staged: staged
HEAD: commit C
--soft: Working: edited ← giữ
Staged: staged ← giữ
HEAD: commit B (lùi 1)
--mixed: Working: edited ← giữ
Staged: CLEAR
HEAD: commit B
--hard: Working: CLEAR ← XOÁ HẾT
Staged: CLEAR
HEAD: commit B⚠️ --hard mất thay đổi không revert dễ. Lỡ → reflog cứu:
git reflog # tìm SHA trước reset
git reset --hard <SHA> # quay lạiReset chỉ dùng cho branch local riêng. Branch shared (main, develop) → git revert (Bài 02), không reset.
Use case reset hằng ngày
# Lỡ commit message sai, chưa push — sửa lại
git reset --soft HEAD~1 # undo commit, giữ thay đổi staged
git commit -m "feat(auth): add JWT refresh (correct message)"
# Lỡ commit thiếu file
git reset --soft HEAD~1
git add forgotten-file
git commit -m "feat(auth): add JWT refresh"
# Hoặc đơn giản hơn: amend
git add forgotten-file
git commit --amend --no-edit⚠️ --amend chỉ trên commit chưa push. Push rồi mà amend thì cần force.
Bisect — tìm commit gây bug
Bug xuất hiện. Code chạy OK 2 tuần trước. Có 200 commit ở giữa. Commit nào gây bug?
git bisect = binary search tự động qua history.
git bisect start
git bisect bad # commit hiện tại có bug
git bisect good v1.2.0 # commit này (2 tuần trước) OK
# Git tự checkout commit giữa
# Bạn test code → quyết định:
git bisect bad # commit này có bug
# hoặc
git bisect good # commit này OK
# Git checkout commit giữa tiếp theo
# Lặp lại
# Sau ~log2(N) bước:
# "abc1234 is the first bad commit"
git bisect reset # quay về branch ban đầu200 commit → ~8 lần test. Cực nhanh.
Tự động hoá với script
Nếu có script test cho bug:
git bisect start HEAD v1.2.0
git bisect run ./test-bug.sh
# Git tự chạy bisect dùng exit code (0 = good, non-zero = bad)Đây là use case khiến atomic commit (Bài 01) cực giá trị. Commit gộp 5 thứ → bisect ra "commit này có bug" — 5 thứ trong commit, không biết thứ nào.
Hotfix production — workflow
Production sập 2h sáng. Quy trình an toàn:
# 1. Branch hotfix từ MAIN (production), KHÔNG từ develop
git switch main
git pull
git switch -c hotfix/payment-500
# 2. Fix tối thiểu — chỉ sửa cái cần để stop bleeding
# KHÔNG refactor, KHÔNG cleanup, KHÔNG add test mới (nếu lo thời gian)
# Chỉ FIX. Atomic commit.
git commit -am "fix(payment): handle null response from gateway"
# 3. Push, mở emergency PR
git push -u origin hotfix/payment-500
# 4. Approve fast-track (1 reviewer thay 2, skip non-critical CI)
# Merge vào main
# 5. Tag release patch
git switch main
git pull
git tag -a v1.2.1 -m "Hotfix: payment null response"
git push origin v1.2.1
# 6. Deploy v1.2.1 lên production
# 7. Verify production OK
# 8. Sáng hôm sau: viết test, postmortem, share teamKhi fix forward khó / lâu: rollback
Friday 4h chiều, bug nghiêm trọng — đừng cố fix. Rollback bằng git revert:
git switch main
git pull
git revert <SHA-of-bad-commit>
git push
# Deploy commit revert5 phút deploy revert > 2 tiếng debug + fix forward + verify. Fix thật vào thứ 2.
Pre-commit hook — chặn lỗi tự động
Hook = script chạy trước commit/push. Chặn rác trước khi vào history.
Setup với pre-commit framework (Python):
pip install pre-commitTạo .pre-commit-config.yaml ở root repo:
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-merge-conflict # check <<<<<<< marker
- id: check-added-large-files # cảnh báo file > 500KB
- id: check-yaml
- id: check-json
- repo: https://github.com/gitleaks/gitleaks
rev: v8.18.0
hooks:
- id: gitleaks # detect AWS key, GitHub token, etc.Cài hook vào repo:
pre-commit installMỗi git commit, hook chạy. Phát hiện secret / conflict marker / file lớn → block commit.
Đưa vào template repo của team — mọi developer onboard có sẵn.
Câu chuyện thực tế (capstone)
Tháng 11/2024, deploy v3.2.0 lên production lúc 3h chiều. Service hoạt động bình thường.
22h đêm, alert: order service báo 500 với rate ~5%. Bug data-dependent — chỉ 1 số order fail.
# Step 1: xem v3.2.0 có gì mới
git log v3.1.0..v3.2.0 --oneline
# 50 commit. Quá nhiều để đọc tay.Rollback toàn bộ v3.2.0? Không — có 1 số tính năng customer đã dùng, rollback gây regression khác.
Bisect:
git bisect start
git bisect bad v3.2.0
git bisect good v3.1.0
# Git checkout giữa, deploy lên staging với data sample → test
git bisect bad # bug có ở đây
# Git checkout giữa tiếp
git bisect good # bug không có
# 6 vòng sau (50 commit / 2^6 ~ 1):
# "abc1234 is the first bad commit"
# Title: "refactor(order): simplify validation logic"git show abc1234Diff cho thấy validation logic miss check order.coupon == null. Edge case.
Hotfix:
git switch main
git switch -c hotfix/order-coupon-null
# Sửa validator, thêm null check
git commit -am "fix(order): handle null coupon in validation"
git push -u origin hotfix/order-coupon-nullFast-track PR → merge → tag v3.2.1 → deploy. 23h45 — production OK.
Hôm sau: viết unit test cho edge case, postmortem chia sẻ team.
Bài học:
- Bisect tiết kiệm hàng giờ — đừng đọc 50 commit tay.
- Atomic commit (Bài 01) làm bisect chính xác. Commit gộp = bisect mơ hồ.
- Hotfix branch từ main, tag patch version, không từ develop.
- Pre-commit hook catch sớm — nhưng không thay được test thật.
Lab
Reflog cứu commit
mkdir /tmp/recovery && cd /tmp/recovery
git init
# Tạo 5 commit
for i in 1 2 3 4 5; do
echo "line $i" >> file.txt
git add . && git commit -m "commit $i"
done
# Lỡ reset hard mất 3 commit
git reset --hard HEAD~3
git log --oneline # chỉ còn 2 commit
# Reflog
git reflog
# 8a1b2c3 HEAD@{0}: reset: moving to HEAD~3
# 9d4e5f6 HEAD@{1}: commit: commit 5
# ...
# Quay lại
git reset --hard HEAD@{1}
git log --oneline # 5 commit đủBisect tự động
mkdir /tmp/bisect-lab && cd /tmp/bisect-lab
git init
echo 'echo OK; exit 0' > test.sh
chmod +x test.sh
git add . && git commit -m "init"
# 5 commit OK
for i in 1 2 3 4 5; do
echo "# v$i" >> test.sh
git commit -am "v$i"
done
# Commit thứ 6 break
echo 'exit 1' >> test.sh
git commit -am "BAD: introduce bug"
# 4 commit OK tiếp
for i in 7 8 9 10; do
echo "# v$i" >> test.sh
git commit -am "v$i"
done
# Bisect tự động
git bisect start HEAD HEAD~10
git bisect run bash test.sh
# Output: "abc1234 is the first bad commit"
git bisect resetPitfalls
git reset --hardkhông kiểm tra reflog trước → tưởng mất. 95% case reflog cứu.- Bisect mà commit gộp → kết quả mơ hồ ("commit này gộp 5 thứ, thứ nào gây bug?"). Atomic commit từ đầu.
- Hotfix từ develop, không phải main → mang theo feature chưa release. Luôn từ main / production tag.
- Friday afternoon fix forward → tệ hơn rollback. Revert trước, fix thứ 2.
- Skip pre-commit hook bằng
--no-verify→ defeat purpose. Chỉ skip khi hook giả mạo (rất hiếm). - Reset trên branch shared → đồng đội loạn. Luôn revert.
Tóm tắt
- Reflog = log local của HEAD. Cứu commit "mất" trong 30 ngày. Mọi case panic — check đây đầu tiên.
- Reset 3 mode:
--softgiữ thay đổi + staged,--mixedgiữ working,--hardXOÁ. Branch shared → revert thay reset. - Bisect = binary search tìm commit gây bug. Auto với
git bisect run. - Hotfix: branch từ main, fix tối thiểu, tag patch, deploy. Friday 4pm = rollback, không fix forward.
- Pre-commit hook chặn secret + lỗi sớm.
gitleaks+pre-commitframework.
🎓 Tổng kết — 3 bài Git
Bạn đã đi qua:
- Bài 01: Workflow hằng ngày — clone, commit, branch, gitignore.
- Bài 02: Làm việc team — merge/rebase, conflict, PR, GitFlow vs Trunk-based.
- Bài 03: Khi rối — reflog, reset, bisect, hotfix.
20 lệnh Git phổ biến nhất. Đủ cho 95% công việc DevOps.
Lúc chưa rõ:
- Lý thuyết Git internal (blob, tree, commit object) — Pro Git book chương 10.
- Submodule, subtree — khi project monorepo / có dependency Git khác.
- Worktree — khi cần checkout 2 branch cùng lúc.
- Custom hook nâng cao, server-side hook.
Những thứ trên hiếm dùng, đọc khi gặp use case cụ thể. Đừng học trước — quên ngay.
Lời khuyên cuối:
- Lab xong 3 bài này trên repo sandbox.
- Setup pre-commit hook + branch protection cho repo team — defense in depth.
- Khi rối — đọc message Git, đừng chạy command random Stack Overflow. Git đa phần nói rõ.
git refloglà người bạn đêm khuya.