Ở Bài 5, rebase phát lại toàn bộ chuỗi commit riêng
của 1 nhánh lên nền mới. Nhưng đôi khi bạn chỉ cần đúng 1 commit cụ thể từ nhánh khác
— không phải cả nhánh. Đó là lúc git cherry-pick phát huy tác dụng.
1. Bài toán: Chỉ Muốn 1 Commit, Không Phải Cả Nhánh
Tình huống kinh điển: một lập trình viên vô tình commit bản sửa lỗi bảo mật khẩn cấp lên nhánh
feature đang phát triển dở (chưa sẵn sàng release). Bạn cần bản vá đó trên
main ngay lập tức, nhưng merge cả feature vào
main sẽ kéo theo toàn bộ code dở dang chưa hoàn thiện.
git cherry-pick <hash> cho phép "nhặt" đúng commit sửa lỗi đó sang
main, bỏ qua mọi commit khác trên feature.
2. Cơ Chế: Tạo Commit Mới Cùng Diff, Khác Hash
Giống hệt nguyên lý rebase đã thấy ở Bài 5: cherry-pick không di chuyển commit gốc. Nó tính diff mà commit đó giới thiệu (so với commit cha của nó), rồi áp diff đó lên đỉnh nhánh hiện tại — tạo ra 1 commit hoàn toàn mới với cha khác, nên hash cũng khác. Commit gốc trên nhánh nguồn vẫn còn nguyên, không bị ảnh hưởng gì.
(cherry picked from commit <hash>) vào cuối message
của commit mới — giúp bất kỳ ai đọc log sau này biết thay đổi này vốn đến từ đâu, dù commit gốc và
bản sao chép có hash hoàn toàn khác nhau.
3. Xử Lý Xung Đột Khi Cherry-pick
Cherry-pick vẫn có thể xung đột — nếu nhánh hiện tại đã thay đổi cùng phần nội dung theo cách khác kể từ điểm phân kỳ, Git không biết chọn phiên bản nào. Về bản chất, cơ chế giải xung đột giống hệt three-way merge đã học ở Bài 4 — chỉ khác base lúc này là commit cha của commit được pick, còn theirs chính là commit đó:
$ git cherry-pick a1b2c3d
# Nếu xung đột:
Auto-merging app.js
CONFLICT (content): Merge conflict in app.js
# Sửa file thủ công, rồi:
$ git add app.js
$ git cherry-pick --continue
feature sang main, rồi sau này vẫn merge
feature vào main, thay đổi đó tồn tại 2 lần trong lịch sử
dưới 2 hash khác nhau (dù nội dung cây thường giống hệt nên Git thường tự merge êm, không báo
conflict). Kết quả: git log hiện 2 commit riêng biệt mang cùng nội dung — gây khó hiểu
khi tra cứu lịch sử về sau. Cân nhắc kỹ trước khi cherry-pick từ 1 nhánh vẫn còn dự định merge đầy
đủ sau này.
4. So Sánh Cherry-pick, Rebase, và Merge
| Tiêu chí | Cherry-pick | Rebase | Merge |
|---|---|---|---|
| Số commit bị ảnh hưởng | Đúng 1 commit chỉ định | Toàn bộ commit riêng của nhánh | Không tạo lại commit nào |
| Commit mới tạo ra | 1 commit mới (hash mới) | N commit mới (N = số commit replay) | 1 merge commit (2 cha) |
| Trường hợp dùng điển hình | Backport 1 hotfix cụ thể sang nhánh khác | Dọn lịch sử thẳng hàng trước khi chia sẻ | Hợp nhất đầy đủ lịch sử 2 nhánh |
Sân chơi tương tác: Cherry-pick Simulator
Kịch bản mẫu đã dựng sẵn 2 nhánh phân kỳ với 1 commit "Fix security bug" trên nhánh
hotfix. Thử git cherry-pick C2 khi đang ở main để xem cách nhặt
commit đó — và xử lý xung đột thực sự xảy ra vì main cũng đã thay đổi nội dung khác kể từ
điểm phân kỳ.
Terminal giả lập
git branch/checkout <tên>git commit -m "..."git cherry-pick <id>git log
Nhật ký
Trắc nghiệm ôn tập
Câu 1: Sau khi git cherry-pick C2 thành công, commit C2 gốc trên nhánh
hotfix có bị thay đổi hay xoá không?
Trắc nghiệm ôn tập
Câu 2: Vì sao commit tạo ra bởi cherry-pick có hash khác hoàn toàn với commit gốc, dù nội dung thay đổi giống hệt nhau?
Trắc nghiệm ôn tập
Câu 3: Khi nào cherry-pick 1 commit KHÔNG gây xung đột (áp dụng êm)?