Git cherry-pick
Xem thửVấn đề cần giải quyết
Một bản sửa lỗi thanh toán vừa được merge vào main. Khách hàng đang dùng bản release/1.0, và cần đúng bản sửa đó, không cần các tính năng mới khác trên main. Merge cả main sang release thì kéo theo mọi thứ. git cherry-pick sao chép một hoặc vài commit cụ thể sang nhánh hiện tại, đúng thứ bạn cần.
Sau bài này, bạn có thể
- Cherry-pick một hoặc nhiều commit và hiểu commit mới khác commit gốc ở đâu.
- Đọc đúng cú pháp dải
a..bvàa^..b. - Xử lý xung đột bằng
--continue,--abortvà--skip.
Tiên quyết: bài Git merge và Git Workflow.
Dùng cơ bản
git switch release/1.0
git cherry-pick a1b2c3d
Git tạo một commit mới trên nhánh hiện tại với cùng nội dung thay đổi nhưng hash khác.
Cách đọc hình. Nửa trên: main có A, B, C; feature có A, B, D, E, tách từ B. Bạn đang ở main và chạy git cherry-pick D. Nửa dưới: main có thêm D' trên C, cùng thay đổi với D nhưng hash khác. Nhánh feature không đổi. Cherry-pick sao chép thay đổi thành commit mới, không di chuyển commit gốc.
Thử và quan sát
Hãy dự đoán: bạn cherry-pick bản sửa lỗi M2 từ main sang nhánh release. Commit trên release có cùng hash với M2 không? Chạy mô phỏng, rồi bấm biến thể gặp xung đột.
Đ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 switch release/1.0 | Chuyển sang nhánh đích. cherry-pick luôn áp commit lên nhánh hiện tại. |
| 2 | git cherry-pick M2 | Git áp thay đổi của M2 thành một commit mới M2' trên release. M2 vẫn ở main và M1 không bị mang theo. |
| 3 | git log --oneline -1 release/1.0 | M2 và M2' mang cùng thông điệp nhưng hash khác nhau, nên đây là hai commit riêng biệt. |
Biến thể: Cherry-pick gặp xung đột
| Bước | Lệnh | Điều xảy ra |
|---|---|---|
| 1 | git cherry-pick M2 | M2 sửa cùng vùng với R1 nên Git dừng giữa chừng và đánh dấu xung đột. Chưa có commit nào được tạo. |
| 2 | git add pay.js | Sau khi sửa tệp xung đột, đánh dấu đã giải quyết bằng git add. |
| 3 | git cherry-pick --continue | Hoàn tất commit M2'. Nếu muốn hủy cả quá trình, dùng --abort; nếu chỉ bỏ commit này và đi tiếp, dùng --skip. |
Giải đáp. Không. Commit mới M2' có cùng thay đổi và thông điệp nhưng hash khác, vì cha của nó khác (R1 thay vì M1). M1 không bị mang theo. Khi có xung đột, Git dừng giữa chừng; bạn sửa tệp, git add, rồi git cherry-pick --continue.
Cherry-pick nhiều commit
git cherry-pick a1b2c3d f4e5d6c
git cherry-pick a1b2c3d..f4e5d6c
git cherry-pick a1b2c3d^..f4e5d6c
Cách đọc hình. Chuỗi commit C1, C2 (a), C3, C4 (b). a..b chọn các commit sau a, tức C3 và C4. a^..b chọn từ chính a, tức C2, C3 và C4. Hãy thêm ^ sau a nếu bạn muốn lấy cả commit đầu.
Các tùy chọn hữu ích
git cherry-pick -x <hash>
git cherry-pick -n <hash>
git cherry-pick -e <hash>
-x ghi dòng "cherry picked from" vào thông điệp, -n áp thay đổi nhưng chưa commit để gộp, -e cho sửa thông điệp trước khi commit.
Khi gặp xung đột
git add <file>
git cherry-pick --continue
git cherry-pick --abort
git cherry-pick --skip
Sau khi sửa tệp xung đột và git add, --continue hoàn tất commit. --abort hủy cả quá trình và quay lại trạng thái trước. --skip chỉ bỏ commit đang xử lý và đi tiếp.
Tình huống thực tế
- Hotfix: đưa một bản vá từ
mainsang nhánh release cũ. - Cứu commit: lấy một commit tốt từ nhánh sắp bỏ.
- Chọn lọc: đưa đúng một tính năng sang nhánh khác.
Lạm dụng cherry-pick dễ tạo commit trùng lặp trên nhiều nhánh, gây rối lịch sử. Hãy dùng có chủ đích, không thay cho merge hay rebase thông thường.
Hiểu lầm thường gặp
- "Cherry-pick di chuyển commit." Nó sao chép; commit gốc vẫn ở nhánh cũ.
- "a..b gồm cả a." Phải viết
a^..bđể gồm a. - "--abort và --skip như nhau."
--aborthủy tất cả,--skipchỉ bỏ một commit.
Tự kiểm tra
- Commit tạo bởi cherry-pick có hash giống commit gốc không?
a..bchọn những commit nào, vàa^..bthì sao?- Cherry-pick bị xung đột, bạn có những lối ra nào?
Đáp án gợi ý. (1) Không, vì cha khác nên hash khác. (2) a..b bắt đầu sau a; a^..b gồm cả a. (3) Sửa rồi --continue, hủy bằng --abort, hoặc bỏ commit đó bằng --skip.
Điều cần nhớ
- Cherry-pick sao chép thay đổi thành commit mới, không di chuyển commit gốc.
- Thêm
^sau a nếu muốn lấy cả commit đầu của dải. - Có ba lối ra khi xung đột:
--continue,--abort,--skip. - Bài sau: gitk và git show.
Nguồn tham khảo
Pro Git, chương 5.3 "Maintaining a Project" (git-scm.com/book); tài liệu git cherry-pick.