Sau Bài 5Bài 6 viết lại/di chuyển lịch sử, bài này trả lời câu hỏi mọi người dùng Git đều từng hoảng loạn hỏi ít nhất 1 lần: "tôi vừa làm hỏng mọi thứ, cứu tôi với!". Tin vui: nhờ mọi object trong Git đều bất biến (Bài 1), gần như mọi thứ đều cứu được — miễn bạn biết công cụ đúng.

1. git reset: Ba Mức Độ "Xoá"

git reset <commit> di chuyển con trỏ branch hiện tại về 1 commit khác — nhưng có 3 chế độ khác nhau về việc Working Directory bị ảnh hưởng ra sao:

Chế độ Con trỏ branch Staging Index Working Directory
--soft Di chuyển Giữ nguyên Giữ nguyên
--mixed (mặc định) Di chuyển Reset về commit đích Giữ nguyên
--hard Di chuyển Reset về commit đích Ghi đè — mất thay đổi chưa commit

Nhớ lại 3 cây từ Bài 2: càng xuống dưới bảng, reset càng "lan" sâu hơn qua 3 cây. --soft chỉ đụng tới HEAD; --hard đụng tới cả 3.

🕳️ Cạm bẫy thường gặp: reset --hard mất vĩnh viễn thứ CHƯA commit
Reflog (mục 3) chỉ cứu được những gì đã từng là 1 commit — vì object trong Git bất biến và reflog theo dõi lịch sử di chuyển HEAD giữa các commit. Thay đổi trong Working Directory chưa bao giờ commit thì chưa từng là object — reset --hard ghi đè chúng và không có cách nào cứu lại. Luôn git stash (mục 4) trước khi chạy reset --hard nếu còn thay đổi dở dang.

2. git revert: Undo An Toàn Cho Nhánh Công Khai

Khác hẳn reset (di chuyển con trỏ, có thể làm mồ côi commit), git revert <commit> tạo ra 1 commit MỚI áp dụng đúng thay đổi ngược lại của commit đó — lịch sử cũ được giữ nguyên vẹn, không branch nào bị "lùi lại". Đây là lựa chọn an toàn khi cần undo trên nhánh đã push, đúng theo Golden Rule đã học ở Bài 5.

3. git reflog: Nhật Ký Cứu Sinh

Mỗi lần HEAD di chuyển — commit, checkout, reset, rebase — Git âm thầm ghi lại vào reflog, một nhật ký cục bộ hoàn toàn tách biệt với các branch ref. Ngay cả sau reset --hard khiến 1 commit trông như "biến mất" (không branch nào trỏ tới, mồ côi giống cơ chế ở Bài 3), reflog vẫn nhớ hash của nó — bạn chỉ cần git branch <tên> <hash-từ-reflog> để "cứu" nó trở lại reachable.

🔬 Đào sâu: Vì sao reflog không đồng bộ qua remote?
Reflog không phải 1 ref thực sự nằm trong .git/refs mà Git đồng bộ khi push/pull — nó là 1 file log riêng (.git/logs/HEAD) chỉ ghi lại lịch sử thao tác trên chính máy bạn. Đây là lý do reflog là công cụ cứu hộ tuyệt vời cho lỗi cục bộ, nhưng vô dụng nếu bạn cần khôi phục thứ gì đó chỉ tồn tại trên máy đồng nghiệp — mặc định reflog cũng tự dọn dẹp các entry cũ (thường sau 90 ngày), không phải kho lưu trữ vĩnh viễn.

4. git stash: Cất Tạm Thay Đổi Chưa Commit

Khi cần chuyển nhánh gấp nhưng thay đổi hiện tại chưa đủ hoàn chỉnh để commit, git stash cất toàn bộ Working Directory + Staging Index vào 1 "ngăn kéo" riêng, trả Working Directory về trạng thái sạch của HEAD. git stash pop lấy lại đúng những gì vừa cất, tiếp tục làm việc như chưa hề rời đi.

Sân chơi tương tác: Demo "Làm Hỏng Rồi Tự Cứu"

Tiếp nối engine từ các bài trước. Thử đúng kịch bản kinh điển: commit vài lần, reset --hard lùi về 1 commit cũ (các commit sau "biến mất" — mồ côi), rồi dùng git reflog tìm lại hash và git branch cứu chúng trở lại.

🧯 Sân chơi tương tác: Undo & Recovery Simulator

Terminal giả lập

  • git commit -m "..."
  • git reset --soft|--mixed|--hard <ref>
  • git revert <ref>
  • git reflog
  • git stash / git stash pop
  • git branch <tên> <ref>

Nhật ký

git-undo-live.js

Trắc nghiệm ôn tập

Câu 1: Bạn git reset --hard HEAD~1 trong khi đang sửa dở 1 file (chưa git add, chưa commit). Chuyện gì xảy ra với phần đang sửa dở đó?

Trắc nghiệm ôn tập

Câu 2: Điều gì khiến git revert an toàn hơn git reset --hard khi cần undo trên 1 nhánh đã push lên remote?

Trắc nghiệm ôn tập

Câu 3: Vì sao git reflog không đồng bộ khi bạn push/pull với đồng nghiệp?

📖 Tài liệu tham khảo / References

Bài viết liên quan trong series

Bài 8: Git Bisect — Tìm Commit Lỗi Bài 6: Cherry-pick — Chọn Lọc Commit Quay lại Lộ trình Series Git