Hủy bỏ thay đổi an toàn: Phân biệt git restore, git reset và git revert
Hướng dẫn hủy thay đổi Git an toàn với git restore, git reset soft/mixed/hard và git revert, kèm mô hình HEAD, staging, working tree và tình huống thực tế.
ShanverseUpdated 10 min read— viewsLoading views
Git
Developer Tools
Ba nhóm lệnh này đều có thể “quay lại”, nhưng tác động rất khác nhau:
git restore khôi phục nội dung file ở Working Tree hoặc Staging Area.
git reset di chuyển branch/HEAD và có thể đồng bộ lại Staging Area, Working Tree.
git revert tạo commit mới đảo ngược một commit cũ, phù hợp với lịch sử đã push.
Comments
Chọn sai có thể khiến thay đổi chưa commit biến mất hoặc lịch sử của team bị rewrite.
Mô hình cần nhớ: HEAD, Staging Area và Working Tree
Mermaid
Loading diagram…
HEAD: commit hiện tại mà branch đang trỏ tới.
Staging Area / Index: ảnh chụp chuẩn bị cho commit tiếp theo.
Working Tree: file bạn đang chỉnh sửa trên ổ đĩa.
Ví dụ một file có thể có:
Bản trong HEAD.
Bản đã stage trong Index.
Bản đang sửa trong Working Tree.
Do đó, “hủy thay đổi” phải trả lời được hai câu hỏi:
Lấy phiên bản từ đâu?
Ghi đè vào vùng nào?
Bảng quyết định nhanh
git restore: Khôi phục nội dung file
restore tập trung vào file, không dùng để di chuyển lịch sử branch.
Bỏ thay đổi chưa stage
Mặc định, Git lấy phiên bản từ Index và ghi vào Working Tree. Thay đổi chưa stage của file sẽ mất.
Khôi phục nhiều file
Khôi phục mọi tracked file trong thư mục hiện tại:
git restore . rất tiện nhưng cũng có thể xóa toàn bộ thay đổi chưa stage của tracked files. Dùng git status và git diff trước.
Unstage một file nhưng giữ nội dung
Thao tác này đưa bản trong Index về theo HEAD, nhưng không xóa nội dung đang có trong Working Tree.
Kiểm tra:
Khôi phục file đã xóa
Nếu file tracked bị xóa nhưng chưa commit:
Nếu việc xóa đã stage:
Lệnh đầu unstage việc xóa; lệnh sau khôi phục file vào Working Tree.
Lấy file từ commit khác
Ví dụ:
Lệnh này thay đổi Working Tree. Hãy review và tạo commit mới nếu muốn ghi nhận việc khôi phục.
Khôi phục cả Index và Working Tree từ source
Đây là thao tác mạnh vì ghi đè cả hai vùng của file. Chỉ dùng sau khi kiểm tra source và diff.
Khôi phục từng phần với --patch
Dạng ngắn:
Git hỏi từng hunk, phù hợp khi muốn giữ một phần thay đổi và bỏ phần còn lại.
git reset: Di chuyển lịch sử local và đồng bộ các vùng
Khi dùng theo commit:
reset thường làm branch hiện tại và HEAD trỏ tới target. Mode quyết định Index và Working Tree có thay đổi hay không.
Mermaid
Loading diagram…
git reset --soft
Kết quả:
Branch/HEAD quay về commit trước.
Thay đổi của commit bị bỏ vẫn nằm trong Staging Area.
Working Tree được giữ nguyên.
Phù hợp khi:
Commit quá sớm.
Muốn gộp lại commit.
Muốn sửa phạm vi commit nhưng giữ mọi thay đổi đã stage.
Ví dụ sửa commit vừa tạo:
Không dùng để rewrite commit đã chia sẻ nếu chưa phối hợp với team.
git reset --mixed
--mixed là mode mặc định:
Tương đương:
Kết quả:
Branch/HEAD quay về target.
Index được đưa về target.
Thay đổi vẫn còn trong Working Tree nhưng trở thành unstaged.
Phù hợp khi muốn bỏ commit local và tự stage lại theo nhóm:
git reset --hard
Kết quả:
Di chuyển branch/HEAD.
Reset Index.
Ghi Working Tree tracked files theo target.
Các thay đổi tracked chưa commit có thể bị mất.
Quy trình an toàn hơn:
Nếu vừa reset nhầm:
reset --hard có xóa untracked files không?
Không nên coi reset --hard là lệnh xóa toàn bộ untracked files. Git có thể xóa untracked file/thư mục nếu chúng cản việc ghi tracked files, nhưng việc dọn untracked thường thuộc về git clean.
Xem trước trước khi clean:
Chỉ xóa khi chắc chắn:
git clean có thể phá hủy file chưa được Git lưu; hãy cực kỳ thận trọng.
Reset theo path khác reset theo commit
Lệnh:
không di chuyển branch hay HEAD. Nó reset entry trong Index, thường dùng để unstage file.
Cách hiện đại, rõ mục đích hơn:
Vì vậy cần phân biệt:
git reset HEAD~1: reset theo commit, có thể di chuyển branch.
git reset path/to/file: reset Index cho path, không di chuyển branch.
git revert: Hoàn tác bằng commit mới
Git tạo một commit mới có patch ngược với commit được chọn.
Lịch sử:
Revert C:
C vẫn tồn tại; R đảo tác động của C.
Đây là lựa chọn mặc định an toàn khi:
Commit đã push.
Branch được nhiều người sử dụng.
Muốn giữ audit trail.
Production cần rollback có thể truy vết.
Revert commit gần nhất
Git mở editor cho commit message. Có thể truyền message theo workflow thông thường sau khi Git tạo nội dung mặc định.
Revert không commit ngay
Dạng ngắn:
Thao tác này áp dụng patch đảo vào Index/Working Tree để bạn kiểm tra hoặc kết hợp nhiều revert trước khi commit.
Revert nhiều commit
Với chuỗi phụ thuộc, thứ tự quan trọng. Thường revert từ commit mới về commit cũ và kiểm tra từng bước.
Một phạm vi:
Xem trước phạm vi bằng git log.
Xử lý conflict khi revert
Hủy quá trình:
Bỏ commit hiện tại trong chuỗi nếu thật sự không cần:
Không chọn ours hoặc theirs máy móc; mục tiêu là tạo trạng thái đúng sau khi đảo thay đổi.
Revert merge commit
Merge commit có nhiều parent, nên cần chỉ định mainline:
-m 1 chọn parent thứ nhất làm mainline để Git tính thay đổi cần đảo. Hãy kiểm tra parent:
Revert merge có ảnh hưởng đến cách Git xem các thay đổi đã được tích hợp. Đừng thực hiện trên production nếu chưa hiểu hậu quả và kế hoạch đưa feature trở lại.
reset và revert: Rewrite hay giữ lịch sử?
Mermaid
Loading diagram…
reset làm branch quên các commit sau target; thường là rewrite local history.
revert giữ commit cũ và thêm commit đảo; lịch sử chỉ tiến về phía trước.
Sau reset một branch đã push, bạn thường phải force push. Điều đó có thể làm mất commit remote của người khác. Nếu việc rewrite được team cho phép, dùng:
Không dùng --force mù quáng và không rewrite branch chung theo phản xạ.
Sáu tình huống thực tế
1. Sửa file sai nhưng chưa stage
2. Stage nhầm file chứa cấu hình local
Nội dung file vẫn còn; chỉ bị bỏ khỏi commit kế tiếp.
3. Commit quá sớm, muốn chia lại
4. Commit message sai
Nếu commit mới nhất chưa push:
Nếu cần gom nhiều commit, dùng interactive rebase. Không cần reset --hard.
5. Commit xấu đã lên branch chung
6. Chỉ muốn lấy lại một file từ trước đó
Ma trận tác động
Checklist trước khi hủy thay đổi
Chạy git status.
Xem git diff và git diff --staged.
Xác định thay đổi ở Working Tree, Index hay commit.
Kiểm tra commit đã push/chia sẻ chưa.
Tạo branch backup trước reset theo commit.
Dùng path cụ thể thay vì . nếu có thể.
Ưu tiên restore --staged để unstage.
Ưu tiên revert cho branch chung.
Không chạy reset --hard với thay đổi chưa backup.
Dùng --force-with-lease chỉ khi rewrite đã được chấp thuận.
Chạy test sau khi restore/revert.
Cheat sheet
Tổng kết
Quy tắc chọn lệnh:
File chưa commit: dùng git restore.
Muốn unstage: dùng git restore --staged.
Commit local chưa chia sẻ: cân nhắc git reset --soft hoặc --mixed.
Muốn xóa cả tracked changes local:--hard chỉ khi đã kiểm tra và backup.
Commit đã push hoặc dùng chung: ưu tiên git revert.
1# Bỏ thay đổi chưa stage của file2gitrestorepath/to/file34# Unstage nhưng giữ code5gitrestore--stagedpath/to/file67# Lấy file từ commit khác8gitrestore--source=<hash>--path/to/file910# Bỏ commit local, giữ staged11gitreset--softHEAD~11213# Bỏ commit local, giữ code unstaged14gitreset--mixedHEAD~11516# Bỏ commit và tracked changes — nguy hiểm17gitreset--hardHEAD~11819# Hoàn tác commit đã chia sẻ20gitrevert<hash>2122# Cứu lại vị trí trước reset23gitreflog
Related posts
More articles with related topics and technologies.
Hướng dẫn Git Aliases và .gitconfig thực chiến: alias an toàn, log đẹp, cấu hình pull/push/rebase, includeIf cho tài khoản work/personal, bảo mật và chia sẻ dotfiles.
Phân tích Gitflow vs GitHub Flow vs Trunk-Based Development: cấu trúc branch, ưu nhược điểm, điều kiện áp dụng, ma trận quyết định và lộ trình chuyển đổi cho từng loại dự án.
Hướng dẫn git worktree từ cơ bản đến thực tế: tạo nhiều working tree, làm feature và hotfix song song, review PR, quản lý list/remove/prune/lock/repair và tránh lỗi phổ biến.