git revert
Xem thửVấn đề cần giải quyết
Một commit vừa được deploy lên production gây lỗi, và cả đội đã kéo nó về. Bạn cần đưa hệ thống về trạng thái trước đó ngay, mà không làm ai bị lệch lịch sử. Xóa commit khỏi nhánh chung là điều không được làm. git revert giải quyết bằng cách thêm một commit đảo ngược thay vì xóa.
Sau bài này, bạn có thể
- Dùng
git revertđể hoàn tác một commit đã chia sẻ. - Dùng
--no-editvà-nđể làm việc gọn khi hoàn tác nhiều commit. - Giải thích khác biệt giữa revert và reset về lịch sử.
Tiên quyết: bài Tổng quan các cách hoàn tác và git log.
Dùng cơ bản
git revert <commit-hash>
git revert --no-edit <commit-hash>
Git tạo một commit mới hủy tác dụng của commit chỉ định và mở trình soạn thảo để xác nhận thông điệp (hoặc bỏ qua bước này với --no-edit). Vì revert thêm lịch sử thay vì xóa, nó an toàn cho nhánh đã chia sẻ.
Thử và quan sát
Hãy dự đoán: C3 là commit lỗi đã push. Sau git revert C3, lịch sử có còn C3 không, và push có cần force không? Chạy mô phỏng, rồi thử biến thể hoàn tác hai commit bằng -n.
Đang tải Animation…
Bảng bước (bản đọc tĩnh của mô phỏng)
| Bước | Lệnh | Điều xảy ra |
|---|---|---|
| 1 | git log --oneline -1 | Xác định commit lỗi: C3 đã nằm trên cả máy bạn lẫn remote. |
| 2 | git revert 9c4e7d1 --no-edit | Git tạo commit mới C4 hủy tác dụng của C3. Nội dung quay về như C2, nhưng C3 vẫn nằm trong lịch sử. |
| 3 | git push origin main | Lịch sử chỉ dài thêm nên push thông thường, không cần force. Đồng đội kéo về bình thường. |
Biến thể: Hoàn tác hai commit bằng -n
| Bước | Lệnh | Điều xảy ra |
|---|---|---|
| 1 | git revert -n C4 | Cờ -n áp dụng phần đảo ngược vào Working Directory và Staging Area nhưng chưa tạo commit. |
| 2 | git revert -n C3 | Revert tiếp commit thứ hai. Cả hai đảo ngược dồn vào cùng một vùng chờ. |
| 3 | git commit -m "Hoàn tác hai commit lỗi" | Một commit duy nhất đảo ngược cả C3 lẫn C4, gọn hơn hai lần revert riêng. |
Giải đáp. C3 vẫn còn trong lịch sử; commit mới C4 hủy tác dụng của nó và nội dung quay về như C2. Push thông thường, không cần force, vì lịch sử chỉ dài thêm. Ở biến thể, -n (--no-commit) áp phần đảo ngược vào Staging Area mà chưa commit, nên bạn gộp nhiều revert thành một commit.
Revert khác reset thế nào
Cách đọc hình. Khung trên là git reset --hard C2: main trỏ lại C2 và C3 không còn nằm trên nhánh, nên ai đã lấy C3 về sẽ lệch lịch sử với bạn. Khung dưới là git revert C3: main trỏ tới C4 mới, C3 vẫn nằm trong lịch sử. Cả hai đưa nội dung về như C2, nhưng reset viết lại lịch sử còn revert thì không.
git resetdời con trỏ nhánh về quá khứ và có thể làm commit rời khỏi nhánh: nguy hiểm khi đã push.git revertgiữ nguyên lịch sử và thêm commit đảo ngược: an toàn cho nhánh chung.
Tình huống thực tế
Một commit lên production gây lỗi:
git revert a1b2c3d --no-edit
git push origin main
Chỉ vài giây, hệ thống trở lại trạng thái trước khi lỗi được đưa vào, và lịch sử vẫn minh bạch về việc đã revert.
Hiểu lầm thường gặp
- "Revert xóa commit lỗi." Revert không xóa gì; commit lỗi vẫn nằm trong lịch sử.
- "Revert luôn thành công." Nếu các commit sau đã sửa cùng vùng, revert có thể gây xung đột và bạn phải giải quyết như khi merge.
- "Revert một merge commit là giống revert commit thường." Với merge commit cần chỉ định cha bằng
-mđể Git biết đảo ngược về phía nào.
Tự kiểm tra
- Vì sao revert an toàn cho nhánh đã chia sẻ còn reset thì không?
- Cờ
-ncủagit revertgiúp gì? - Sau revert, nội dung giống commit nào và lịch sử có gì khác so với trước?
Đáp án gợi ý. (1) Revert chỉ thêm commit nên lịch sử của mọi người vẫn khớp; reset viết lại lịch sử đã chia sẻ. (2) Áp đảo ngược mà chưa commit, để gộp nhiều lần revert thành một commit. (3) Giống commit trước commit lỗi; lịch sử có thêm một commit đảo ngược.
Điều cần nhớ
- Revert thêm commit đảo ngược, không xóa gì.
--no-editbỏ bước soạn thông điệp;-ngộp nhiều revert thành một commit.- Nhánh đã chia sẻ: dùng revert, không dùng reset.
- Bài sau: git reset và ba chế độ của nó.
Nguồn tham khảo
Tài liệu git-revert (git-scm.com/docs/git-revert); Pro Git, chương 7.7 (git-scm.com/book).