Giao diện
05. Reset, Revert & Cherry-pick
IMPORTANT
Ba lệnh này đều là selective history tools: chúng giúp bạn can thiệp vào lịch sử Git theo cách có chủ đích, nhưng mức độ an toàn hoàn toàn khác nhau.
git reset: phẫu thuật lịch sử local. Nhanh, mạnh, nhưng dễ gây tai nạn nếu đụng vào lịch sử đã chia sẻ.git revert: undo an toàn cho shared history. Không xóa lịch sử, chỉ thêm một commit đảo ngược.git cherry-pick: cấy ghép chọn lọc. Lấy đúng commit bạn cần sang branch hiện tại, nhưng không phải chiến lược thay thế merge/rebase.
Bài này giải quyết vấn đề gì?
Trong công việc thật, bạn thường gặp 3 tình huống:
- Lịch sử local đang bẩn trước khi mở PR → cần bỏ vài commit WIP, nhưng vẫn giữ code để dọn dẹp lại.
- Một commit xấu đã lên production hoặc
main→ cần hoàn tác an toàn mà không phá lịch sử cả team. - Một hotfix nằm trên branch khác → cần mang đúng commit fix đó sang branch hiện tại mà không kéo theo cả đống thay đổi khác.
Ba tình huống này nghe giống nhau ở bề mặt: “undo” hoặc “lấy một phần lịch sử”.
Nhưng nếu dùng sai công cụ, bạn có thể:
- làm lệch lịch sử branch shared,
- tạo duplicate commit khó theo dõi,
- hoặc tự xóa mất local work.
Mental model: chọn đúng dao mổ
text
Bạn muốn làm gì?
1) "Tôi muốn quay lại lịch sử LOCAL của chính mình"
-> git reset
2) "Tôi muốn hủy tác dụng của một commit đã chia sẻ với team"
-> git revert
3) "Tôi chỉ muốn lấy MỘT VÀI commit từ branch khác"
-> git cherry-pickReset vs Revert: khác nhau ở triết lý
| Lệnh | Giải quyết vấn đề gì? | Tác động lên lịch sử | Khi nào phù hợp? | Hồ sơ an toàn |
|---|---|---|---|---|
git reset | Dọn lịch sử local, bỏ commit local, unstage/uncommit | Viết lại vị trí HEAD, có thể làm commit trở nên unreachable | Local branch riêng của bạn, chưa push hoặc chưa chia sẻ | Nguy hiểm nếu đụng shared history |
git revert | Hoàn tác một commit đã tồn tại trong lịch sử shared | Thêm commit mới đảo ngược thay đổi | main, production, branch đã push cho team | An toàn cho collaboration |
git cherry-pick | Lấy đúng commit cần thiết từ branch khác | Tạo commit mới với nội dung tương tự trên branch hiện tại | Hotfix, backport, chọn lọc fix | An toàn vừa phải, nhưng dễ bị lạm dụng |
1) git reset: local-history surgery
git reset giải quyết vấn đề gì?
git reset dùng khi bạn muốn thay đổi lại lịch sử local của chính branch hiện tại:
- bỏ commit WIP trước khi mở PR,
- unstage file đã add nhầm,
- quay HEAD về commit cũ hơn,
- dọn lại lịch sử local cho sạch trước khi chia sẻ.
Nói ngắn gọn: reset không phải “undo cho team”, mà là dao mổ cho lịch sử local của riêng bạn.
Ba chế độ cần nhớ
bash
git reset --soft HEAD~1
git reset --mixed HEAD~1
git reset --hard HEAD~1| Chế độ | Commit history | Staging area | Working directory | Dùng khi nào |
|---|---|---|---|---|
--soft | Lùi HEAD | Giữ nguyên | Giữ nguyên | Muốn gộp/sửa lại commit message hoặc commit lại cho đẹp |
--mixed (mặc định) | Lùi HEAD | Bỏ stage | Giữ nguyên | Muốn uncommit + chọn stage lại từ đầu |
--hard | Lùi HEAD | Xóa | Xóa | Muốn bỏ sạch local changes và commit local |
Ví dụ thực tế: reset local WIP trước khi dọn PR
Bạn đang ở branch feature/payment-timeout và có 3 commit local:
text
A - commit sạch cuối cùng
B - wip: thử timeout approach 1
C - wip: thêm debug logs
D - wip: sửa tạm testBạn nhận ra trước khi mở PR, mình cần dọn lại thành 1 commit tử tế.
bash
git reset --mixed HEAD~3Kết quả:
- branch quay lại commit
A, - toàn bộ thay đổi từ
B,C,Dvẫn nằm trong working directory, - bạn có thể stage lại theo từng phần và tạo commit sạch hơn.
Đây là use case rất chuẩn của reset: cleanup local WIP trước khi chia sẻ.
Khi nào KHÔNG nên dùng git reset?
Không dùng reset khi:
- commit đã push lên
mainhoặc branch shared, - branch đang được nhiều người cùng làm,
- bạn không chắc commit đó đã được chia sẻ hay chưa,
- bạn định “undo production bug” bằng cách rewrite history.
CAUTION
Không reset shared history rồi force-push như một thói quen.
Với team workflow nghiêm túc, đó là cách tạo incident collaboration nhanh nhất.
Nếu dùng sai git reset, recovery thế nào?
Tình huống điển hình: bạn lỡ git reset --hard và mất local commit.
Bước 1: xem lại dấu vết bằng reflog
bash
git reflogVí dụ:
text
8c2f1ab HEAD@{0}: reset: moving to HEAD~2
41d9e77 HEAD@{1}: commit: fix(payment): handle timeout retryBước 2: cứu lại commit bằng reset hoặc branch tạm
bash
git reset --hard 41d9e77Hoặc an toàn hơn:
bash
git switch -c rescue-reset-accident 41d9e77Checklist trước khi reset
- Branch này có phải branch local riêng của mình không?
- Commit này đã push chưa?
- Mình muốn giữ code hay xóa sạch code?
- Nếu lỡ tay, mình có biết dùng
git reflogđể quay lại không?
2) git revert: shared-history-safe undo
git revert giải quyết vấn đề gì?
git revert dùng khi một commit đã đi vào lịch sử chia sẻ và bạn cần hủy tác dụng của nó mà không rewrite history.
Đây là công cụ đúng trong các tình huống như:
- commit lỗi đã lên
main, - production đang cháy và bạn cần rollback an toàn,
- team đã pull commit đó rồi nên không thể giả vờ “nó chưa từng tồn tại”.
Cách hoạt động
bash
git revert <bad-commit-sha>Git sẽ tạo ra một commit mới có thay đổi ngược với commit xấu.
text
A - B - C - D
^
commit xấu
git revert C
A - B - C - D - E
^
E đảo ngược tác dụng của CLịch sử vẫn minh bạch:
- commit xấu vẫn còn để audit,
- commit revert cho thấy team đã rollback như thế nào,
- không ai bị lệch lịch sử.
Ví dụ thực tế: revert bad production commit
Giả sử commit 9f31abc đã được deploy lên production và gây lỗi login.
bash
git switch main
git pull origin main
git revert 9f31abc
git push origin mainĐây là lựa chọn đúng vì:
- production cần rollback nhanh,
mainlà shared branch,- mọi người khác có thể pull bình thường,
- CI/CD và audit trail vẫn rõ ràng.
Khi nào KHÔNG nên dùng git revert?
Không nên dùng revert khi:
- commit đó chỉ mới ở local của bạn,
- bạn chỉ muốn dọn WIP trước khi mở PR,
- bạn đang cố “lau sạch” lịch sử local riêng của mình,
- bạn dùng revert liên tục để vá một workflow branch quá lộn xộn thay vì sửa chiến lược branch.
Nói cách khác:
- local cleanup → nghĩ tới
reset - shared undo → nghĩ tới
revert
Nếu dùng sai git revert, recovery thế nào?
Tình huống điển hình: bạn revert nhầm commit.
Giải pháp thường là revert chính commit revert đó:
bash
git log --oneline
git revert <sha-cua-commit-revert>Điều này tạo thêm một commit mới để khôi phục lại thay đổi trước đó.
TIP
Nếu bạn đã revert một commit trên main rồi sau đó phát hiện rollback là sai, đừng reset main để xóa revert.
Hãy revert chính commit revert đó để lịch sử vẫn an toàn và minh bạch.
3) git cherry-pick: selective transplant, không phải branch strategy
git cherry-pick giải quyết vấn đề gì?
git cherry-pick dùng khi bạn cần đem đúng commit đang cần từ branch khác sang branch hiện tại.
Use case chuẩn:
- lấy một hotfix từ branch
hotfix/...sangmain, - backport một fix từ
mainxuống release branch cũ, - chọn đúng một commit sửa bug mà không merge cả branch nguồn.
Đây là hành động cấy ghép chọn lọc, không phải cách thay thế merge/rebase trong workflow hằng ngày.
Cách dùng cơ bản
bash
git cherry-pick <commit-sha>Git sẽ tạo một commit mới trên branch hiện tại với nội dung gần tương đương commit gốc.
Ví dụ thực tế: cherry-pick hotfix
Giả sử team fix lỗi timeout đăng nhập trên branch hotfix/login-timeout, và commit fix là a1b2c3d.
Bạn cần đưa đúng fix đó vào main ngay, nhưng không muốn merge toàn bộ hotfix branch vì branch còn thêm log tạm và vài thay đổi chưa review.
bash
git switch main
git pull origin main
git cherry-pick a1b2c3d
git push origin mainĐây là use case đẹp của cherry-pick: lấy đúng một commit cứu hỏa.
Khi nào KHÔNG nên dùng git cherry-pick?
Không nên dùng cherry-pick khi:
- bạn định dùng nó thay cho merge/rebase thường xuyên,
- bạn muốn đồng bộ cả một branch dài hạn,
- bạn không hiểu commit phụ thuộc vào context nào,
- commit bạn chọn phụ thuộc vào nhiều commit trước đó nhưng bạn chỉ pick một mình nó.
WARNING
cherry-pick không phải branch strategy.
Nếu lạm dụng, bạn sẽ tạo ra nhiều commit “na ná nhau nhưng SHA khác nhau”, khiến lịch sử khó đọc, khó trace, và dễ conflict về sau.
Nếu cherry-pick bị conflict hoặc dùng sai, recovery thế nào?
Nếu conflict xảy ra:
bash
git cherry-pick --abortĐiều này đưa bạn về trạng thái trước khi bắt đầu cherry-pick.
Nếu bạn đã cherry-pick xong nhưng nhận ra pick nhầm:
- nếu commit vẫn local, có thể
git reset --hard HEAD~1chỉ khi chưa push và bạn chắc chắn branch là private/local; - nếu đã push lên shared branch, hãy
git revert <sha-cua-commit-cherry-pick>.
Nguyên tắc sống còn khi cherry-pick
Trước khi pick, hãy tự hỏi:
- Commit này có tự đứng độc lập được không?
- Nó có phụ thuộc vào commit nền nào khác không?
- Mình có đang dùng cherry-pick để né một branch strategy đang sai không?
So sánh nhanh bằng tình huống thật
Tình huống A: “Tôi có 3 commit WIP local và muốn dọn lại trước khi mở PR”
Dùng: git reset --mixed HEAD~3
Vì sao: bạn đang làm sạch lịch sử local, chưa động vào shared history.
Không dùng: git revert vì bạn chưa cần tạo audit trail cho team.
Tình huống B: “Một commit lỗi đã lên production, cần rollback an toàn”
Dùng: git revert <bad-commit>
Vì sao: đây là shared history, rollback phải an toàn và minh bạch.
Không dùng: git reset trên main.
Tình huống C: “Có một hotfix trên branch khác, tôi chỉ cần đúng commit fix đó”
Dùng: git cherry-pick <hotfix-commit>
Vì sao: bạn cần selective transplant, không cần merge cả branch.
Không dùng: cherry-pick như cách chính để đồng bộ branch lâu dài.
Recovery playbook: nếu lỡ dùng sai thì làm gì?
Bạn lỡ reset --hard và mất commit local
bash
git reflog
git switch -c rescue-branch <sha-cu>Hoặc:
bash
git reset --hard <sha-cu>Bạn revert nhầm commit trên shared branch
bash
git log --oneline
git revert <sha-cua-commit-revert>Bạn cherry-pick bị conflict và chưa muốn giải quyết
bash
git cherry-pick --abortBạn cherry-pick nhầm lên shared branch và đã push
bash
git revert <sha-cua-commit-da-pick>Quy tắc nhớ nhanh
TIP
Hãy nhớ câu này:
- Reset = local-history surgery
- Revert = shared-history-safe undo
- Cherry-pick = selective transplant
Nếu bạn nhớ đúng ba câu này, bạn sẽ tránh được phần lớn tai nạn Git liên quan tới undo và recovery.
🧠 Quiz
Câu 1: Khi một commit lỗi đã được push lên main, lựa chọn an toàn nhất là gì?
- [ ] A.
git reset --hard HEAD~1rồi force-push - [x] B.
git revert <bad-commit-sha> - [ ] C.
git cherry-pick <bad-commit-sha> - [ ] D.
git checkout <bad-commit-sha>
💡 Giải thích:
mainlà shared history.reverttạo commit mới để rollback an toàn mà không phá lịch sử của cả team.
Câu 2: Khi nào git reset là lựa chọn phù hợp nhất?
- [x] A. Khi dọn local WIP trước khi mở PR trên branch riêng của bạn
- [ ] B. Khi cần rollback production trên
main - [ ] C. Khi cần mang hotfix từ branch khác sang
main - [ ] D. Khi muốn giữ nguyên shared history nhưng đảo ngược một commit đã push
💡 Giải thích:
resetsinh ra để chỉnh lịch sử local của bạn. Nó không phải công cụ an toàn cho branch shared.
Câu 3: Phát biểu nào đúng nhất về git cherry-pick?
- [ ] A. Đây là cách tốt nhất để đồng bộ hai branch dài hạn
- [x] B. Đây là công cụ cấy ghép commit chọn lọc, thường dùng cho hotfix hoặc backport
- [ ] C. Đây là phiên bản an toàn hơn của merge
- [ ] D. Đây là lệnh dùng để xóa commit khỏi lịch sử
💡 Giải thích:
cherry-pickrất tốt cho tình huống chọn đúng một commit cần thiết, nhưng không nên biến nó thành branch strategy chính.
Câu 4: Nếu lỡ git reset --hard làm mất commit local, bước recovery đầu tiên nên là gì?
- [ ] A. Clone lại repo
- [x] B. Chạy
git reflogđể tìm SHA trước khi reset - [ ] C. Chạy
git revert HEAD - [ ] D. Tạo pull request mới
💡 Giải thích:
refloglà nơi lần lại dấu vết HEAD sau reset, rebase, checkout và nhiều thao tác nguy hiểm khác.