Hướng dẫn

git cherry-pick: cách chuyển một commit sang nhánh khác

Cập nhật: · Skillaria.Top

git cherry-pick lấy thay đổi của một commit chỉ định và tạo trên nhánh hiện tại một commit mới có cùng diff và thông điệp nhưng hash khác. Chuyển sang nhánh đích rồi chạy git cherry-pick <hash>; nếu có xung đột, sửa tệp, chạy git add và git cherry-pick --continue.

git cherry-pick chuyển thay đổi của một commit cụ thể sang nhánh hiện tại, không gộp cả nhánh. Git tính diff của commit được chọn so với cha của nó, áp dụng vào HEAD hiện tại và tạo commit mới — với cùng thông điệp và tác giả, nhưng hash khác, vì commit có cha khác.

Tình huống điển hình: trong nhánh tính năng bạn đã sửa một lỗi, và bản sửa này cần gấp cho nhánh phát hành, nơi tính năng chưa hoàn thiện. Không thể gộp cả nhánh, nhưng có thể chuyển đúng một commit.

Chuyển một commit sang nhánh khác

Trước hết tìm hash của commit cần chuyển, sau đó chuyển sang nhánh đích và chạy cherry-pick.

# 1. Tìm commit trong nhánh feature
git log --oneline feature
a1b2c3d Sửa lỗi chia cho 0 khi tính giảm giá
9f8e7d6 Thêm màn hình thanh toán mới

# 2. Chuyển sang nhánh đích
git switch release-1.4

# 3. Chuyển commit
git cherry-pick a1b2c3d
[release-1.4 4e5f6a7] Sửa lỗi chia cho 0 khi tính giảm giá
 Date: Mon Oct 5 14:12:03 2026 +0300
 1 file changed, 3 insertions(+), 1 deletion(-)

Lưu ý: commit mới có hash 4e5f6a7, không phải a1b2c3d. Đây là hai đối tượng khác nhau nhưng có cùng thay đổi. Nhánh gốc feature không bị thay đổi.

Thay vì hash, có thể truyền bất kỳ tham chiếu nào tới commit: git cherry-pick feature lấy commit cuối của nhánh feature, git cherry-pick feature~2 — commit thứ ba tính từ cuối.

Chuyển nhiều commit

Liệt kê

Có thể truyền danh sách hash — Git sẽ áp dụng theo thứ tự đã cho và tạo ra số commit tương ứng.

git cherry-pick a1b2c3d 7c8d9e0 3f4a5b6

Thứ tự quan trọng: nếu commit thứ hai phụ thuộc vào commit thứ nhất mà bạn liệt kê ngược lại, sẽ xảy ra xung đột hoặc code hỏng. An toàn nhất là liệt kê từ cũ đến mới.

Theo khoảng

Với các commit liên tiếp có thể dùng cú pháp khoảng. Ở đây có một cái bẫy mà hầu như ai cũng gặp: A..B nghĩa là «các commit có thể đi tới từ B nhưng không đi tới từ A», tức là chính A không nằm trong khoảng.

# Chuyển các commit SAU A cho tới B (gồm cả B)
git cherry-pick A..B

# Chuyển cả A và mọi commit tới B (gồm cả B)
git cherry-pick A^..B

Nếu cần chuyển tất cả những gì có trong feature nhưng không có trong main, có thể viết git cherry-pick main..feature. Nhưng nếu commit quá nhiều, hãy cân nhắc: có lẽ bạn cần rebase hoặc merge chứ không phải cherry-pick.

Các cờ hữu ích của git cherry-pick

CờTác dụngKhi nào cần
-xThêm dòng «(cherry picked from commit …)» vào thông điệpKhi chuyển bản sửa giữa các nhánh công khai — để thấy nguồn gốc
-n, --no-commitÁp dụng thay đổi vào index và thư mục làm việc mà không commitMuốn gom nhiều commit thành một hoặc sửa trước khi commit
-e, --editMở trình soạn thảo để sửa thông điệpCần điều chỉnh thông điệp cho phù hợp với nhánh đích
-s, --signoffThêm dòng Signed-off-byDự án yêu cầu DCO
-m 1Chỉ định cha «chính» cho commit gộpKhi chuyển commit gộp (merge)
--ffDi chuyển nhánh tới trước nếu HEAD là cha của commit được chọnHiếm; tránh tạo bản sao trùng lặp
--allow-emptyCho phép chuyển các commit rỗng ngay từ đầuCác commit rỗng mang tính kỹ thuật

Vì sao cần -x

Sau nửa năm, ai đó thấy một bản sửa trong nhánh phát hành và muốn biết nó đến từ đâu. Dòng do -x thêm vào trả lời ngay câu hỏi đó:

git cherry-pick -x a1b2c3d
git log -1
    Sửa lỗi chia cho 0 khi tính giảm giá

    (cherry picked from commit a1b2c3d4e5f60718293a4b5c6d7e8f9012345678)

Hãy dùng -x khi commit gốc đã được công bố. Nếu chuyển từ nhánh cục bộ mà bạn sẽ xóa sau đó, liên kết sẽ dẫn tới đâu đó không tồn tại — khi đó không cần cờ này.

Gom nhiều commit thành một: -n

git cherry-pick -n a1b2c3d 7c8d9e0
git status          # thay đổi của cả hai commit nằm trong index
git commit -m "Sửa tính toán giảm giá"

Xung đột khi cherry-pick và cách xử lý

Xung đột xảy ra khi thay đổi của commit không khớp với mã hiện tại: trong nhánh đích, các dòng đó đã khác. Git dừng lại và thông báo.

git cherry-pick a1b2c3d
Auto-merging src/discount.js
CONFLICT (content): Merge conflict in src/discount.js
error: could not apply a1b2c3d... Sửa lỗi chia cho 0 khi tính giảm giá
hint: After resolving the conflicts, mark them with
hint: "git add/rm <pathspec>", then run
hint: "git cherry-pick --continue".

Sau đó có ba cách:

  1. Giải quyết và tiếp tục. Mở tệp, xóa các dấu <<<<<<<, =======, >>>>>>>, giữ lại phiên bản đúng, sau đó git add src/discount.js và git cherry-pick --continue.
  2. Bỏ qua commit. Khi chuyển một danh sách, git cherry-pick --skip bỏ qua commit có vấn đề và chuyển sang commit tiếp theo.
  3. Hủy tất cả. git cherry-pick --abort đưa nhánh về trạng thái trước khi thao tác bắt đầu.

Còn có git cherry-pick --quit: nó dừng thao tác nhưng giữ lại các commit đã áp dụng và trạng thái tệp hiện tại. Hữu ích khi bạn đã chuyển thành công một nửa danh sách và muốn xử lý phần còn lại bằng tay.

Nếu xung đột lặp lại liên tục (ví dụ bạn thường xuyên chuyển bản sửa sang nhiều nhánh phát hành), hãy bật git config rerere.enabled true — Git sẽ ghi nhớ cách bạn giải quyết xung đột và tự áp dụng lần sau.

Chuyển commit gộp (merge)

Commit gộp có hai cha, và Git không biết nên tính diff so với cha nào. Nếu không có gợi ý, bạn sẽ gặp lỗi:

git cherry-pick 5d6e7f8
error: commit 5d6e7f8 is a merge but no -m option was given.

Cờ -m 1 nghĩa là: tính thay đổi so với cha thứ nhất — thường là nhánh đã được gộp vào. Như vậy bạn chuyển toàn bộ những gì nhánh đó mang theo trong một commit.

git cherry-pick -m 1 5d6e7f8

Lưu ý: sau thao tác này Git không coi nhánh là đã gộp, nên khi merge sau này, nó có thể cố áp dụng lại cùng các thay đổi.

git cherry-pick và git apply: khác nhau thế nào

git apply thường được nhắc cùng cherry-pick vì cả hai đều «áp dụng thay đổi». Nhưng chúng làm việc với dữ liệu đầu vào khác nhau.

git cherry-pickgit apply
NguồnCommit có trong khoTệp patch (diff)
Tạo commitCó (nếu không dùng -n)Không, chỉ thay đổi tệp (với --index thì cả index)
Giữ tác giả và thông điệpCóKhông, patch không chứa chúng
Xung độtGộp ba chiều với dấu xung độtMặc định chỉ từ chối; với --3way sẽ có dấu xung đột
Cần chung khoCó, commit phải có sẵn cục bộKhông, có thể gửi patch bằng bất kỳ cách nào

Ví dụ dùng git apply khi commit không có trong kho của bạn — đồng nghiệp gửi cho bạn một tệp diff:

# Đồng nghiệp làm
git diff main..fix-discount > fix.patch

# Bạn kiểm tra và áp dụng
git apply --check fix.patch   # kiểm tra xem patch có áp được không
git apply fix.patch           # áp vào thư mục làm việc
git apply --3way fix.patch    # khi có sai lệch — với dấu xung đột

Nếu muốn gửi nguyên commit kèm tác giả và thông điệp, hãy dùng cặp git format-patch và git am: lệnh đầu lưu commit thành tệp, lệnh sau tạo lại commit từ các tệp đó. Về bản chất, đây là cherry-pick «qua thư».

Nhân tiện, cũng có thể cherry-pick commit từ kho khác: thêm kho đó như remote, chạy git fetch, và các commit sẽ có thể truy cập bằng hash.

Khi nào cherry-pick là ý tưởng tồi

Cherry-pick tạo bản sao: một thay đổi tồn tại trong lịch sử dưới hai hash. Với lượng nhỏ thì điều này bình thường, nhưng có những tình huống nó gây hại.

  • Chuyển hàng chục commit. Nếu bạn chuyển gần như toàn bộ nhánh, đó là việc của rebase hoặc merge. Cherry-pick sẽ cho lịch sử dài đầy bản trùng và nhiều xung đột hơn.
  • Chuyển qua lại hai chiều. Commit được chuyển từ feature sang main, sau đó feature được gộp vào main. Git thường xử lý được (thay đổi giống nhau), nhưng nếu sau khi chuyển có chỉnh sửa lại các dòng đó, sẽ xảy ra xung đột vô cớ.
  • Commit phụ thuộc lẫn nhau. Commit dùng một hàm được thêm bởi hai commit trước đó. Cherry-pick chỉ chuyển đúng commit đó, và code trong nhánh đích sẽ không build được. Luôn kiểm tra build và test sau khi chuyển.

Thực hành tốt với hotfix là sửa ngay trong nhánh được hỗ trợ lâu nhất và gộp lên các nhánh mới hơn. Khi đó cherry-pick chỉ là trường hợp ngoại lệ.

Cách hoàn tác cherry-pick

Nếu thao tác vẫn đang dang dở (có xung đột) — git cherry-pick --abort. Nếu commit đã được tạo và chưa đẩy lên máy chủ, hãy gỡ nó:

git reset --hard HEAD~1

Nếu commit đã được push lên nhánh chung, không được viết lại lịch sử — hãy tạo commit đảo ngược:

git revert 4e5f6a7

Và nếu bạn đã reset rồi đổi ý, commit bị mất có thể tìm lại qua git reflog.

Cách kiểm tra commit đã được chuyển chưa

Vì hash khác nhau, git branch --contains thông thường không giúp được. Git có thể so sánh các commit theo nội dung thay đổi (patch-id):

git cherry -v release-1.4 feature
- a1b2c3d Sửa lỗi chia cho 0 khi tính giảm giá
+ 9f8e7d6 Thêm màn hình thanh toán mới

Dấu trừ nghĩa là thay đổi tương đương đã có trong release-1.4, dấu cộng — chưa có. Cách khác là git log --cherry-pick --oneline release-1.4...feature, lệnh này ẩn các commit «song sinh». Nếu bạn đã dùng -x, có thể tìm theo thông điệp: git log --grep="cherry picked from commit a1b2c3d".

Tóm lại

git cherry-pick là công cụ chuyên biệt: đưa một hotfix vào bản phát hành, lấy một commit hữu ích từ nhánh bị bỏ dở, hoặc khôi phục một commit lỡ tạo nhầm chỗ. Hãy nhớ ba điều: commit mới nhận hash mới, khoảng A..B không bao gồm A, và với nhánh công khai nên thêm -x. Nếu cần chuyển không phải commit mà là diff trong một tệp, hãy dùng git apply. Rebase, stash, cherry-pick và thẻ được giải thích chi tiết qua thực hành trong khóa học Git.

Câu hỏi thường gặp

Cherry-pick có thay đổi nhánh gốc không?

Không. Commit gốc vẫn ở nguyên vị trí, còn trong nhánh hiện tại xuất hiện một bản sao với hash mới.

Có thể cherry-pick commit từ nhánh từ xa không?

Có. Chạy git fetch, khi đó commit có thể dùng theo hash hoặc qua origin/tên-nhánh, và có thể truyền vào git cherry-pick.

Vì sao sau cherry-pick hash commit khác?

Hash phụ thuộc vào cha, thời gian commit và nội dung cây tệp. Bản sao có cha khác và thời gian người commit khác, nên hash cũng khác.

Tác giả commit có được giữ lại khi cherry-pick không?

Có, tác giả và ngày tác giả được lấy từ commit gốc, còn người commit là bạn với thời gian hiện tại.

Cherry-pick báo commit rỗng thì làm gì?

Điều đó nghĩa là thay đổi này đã có trong nhánh. Bỏ qua bằng git cherry-pick --skip, hoặc giữ lại dưới dạng commit rỗng bằng git commit --allow-empty.

Học chủ đề này qua video

Chủ đề này được giảng chi tiết kèm thực hành trong khóa học “Git с нуля до уверенного разработчика”.

Đến khóa học