Hai khái niệm hay bị hiểu lơ mơ và thường đi cùng nhau: git cherry-pick để bê một commit lẻ sang nhánh khác, và trạng thái detached HEAD mà git bisect (bài trước) hay git checkout <sha> đẩy bạn vào. Cả hai đều sáng tỏ ngay khi bạn nhớ mô hình từ đầu sê-ri: commit là bất biến, băm theo nội dung và parent; còn HEAD chỉ là một con trỏ. Bài này đo thật cả hai trong Git 2.39.
cherry-pick: chép riêng một commit
Tình huống: nhánh tinh-nang có ba commit, nhưng một trong số đó là một hotfix khẩn cấp cần lên main ngay — không thể chờ merge cả nhánh (hai commit kia chưa xong). git cherry-pick <sha> lấy đúng commit đó, áp diff của nó lên nhánh hiện tại, và tạo một commit mới:
git switch main
git cherry-pick 5aa73b3 # commit hotfix trên tinh-nang
Vì commit mới có parent khác (đỉnh của main thay vì đỉnh của tinh-nang), nó có SHA khác — giống hệt cơ chế của rebase (bài git-06): cùng nội dung, khác danh tính. Đây là cherry-pick "chép" chứ không "di chuyển".

Hình 1: cherry-pick áp diff một commit lên nhánh hiện tại (tạo SHA mới). detached HEAD là khi HEAD trỏ thẳng vào commit thay vì qua một nhánh — commit tạo ở đó dễ mồ côi nếu không đặt tên.
Đo thật: main ban đầu chỉ có f.txt. Sau git cherry-pick 5aa73b3, main có thêm fix.txt — nhưng không có g.txt (file của các commit tính năng kia). Đúng như mong muốn: chỉ một commit được bê sang.

Hình 2: cherry-pick tạo commit aea78f7 trên main từ commit gốc 5aa73b3 — SHA khác, chỉ mang fix.txt. Bên dưới: detached HEAD tại f35e437, commit 113ddcf tạo trong đó bị cảnh báo "leaving 1 commit behind", và được cứu bằng git branch.
detached HEAD: HEAD trỏ thẳng vào commit
Bình thường chuỗi con trỏ là HEAD → nhánh → commit (bài git-03). Khi bạn git checkout <sha> (hay checkout một tag, hay ở giữa git bisect), Git đặt HEAD trỏ thẳng vào commit, bỏ qua khâu nhánh: HEAD → commit. Đó là "detached HEAD" — HEAD "rời" khỏi nhánh. Git in hẳn một đoạn giải thích và git status xác nhận HEAD detached at f35e437.
Ở trạng thái này bạn vẫn xem, sửa, commit được bình thường. Vấn đề duy nhất: commit bạn tạo không thuộc nhánh nào. Đo thật — tạo commit 113ddcf trong detached HEAD, rồi git switch sang nhánh khác. Git cảnh báo rõ ràng:
Warning: you are leaving 1 commit behind, not connected to
any of your branches:
113ddcf thu nghiem trong detached
Commit 113ddcf giờ mồ côi. Nó chưa mất (còn trong reflog và kho, bài git-07), nhưng nếu bỏ qua cảnh báo này và quên SHA, tới lần gc là mất thật. Cách giữ lại đơn giản:
git branch giu-lai 113ddcf # đặt tên nhánh cho commit -> an toàn
# hoặc ngay khi còn trong detached: git switch -c ten-nhanh-moi
Sau git branch giu-lai 113ddcf, commit nằm trên một nhánh có tên, hết mồ côi. detached HEAD không đáng sợ — nó chỉ là HEAD không gắn nhãn nhánh. Biết điều đó thì git bisect, xem lại code ở một tag cũ, hay thử nghiệm nhanh trên một commit đều thành chuyện thường.
Khi nào dùng cherry-pick — và khi nào đừng
Hợp lý: bê hotfix từ nhánh phát triển sang nhánh release đang bảo trì; lấy một commit hay từ PR của người khác mà chưa muốn merge cả nhánh; khôi phục một commit lẻ đã bị mất nhánh.
Nên tránh: cherry-pick hàng loạt commit qua lại giữa các nhánh dài hạn. Vì mỗi cherry-pick tạo commit mới với cùng nội dung, bạn sẽ có hai commit khác SHA nhưng cùng diff trên hai nhánh. Khi hai nhánh đó merge sau này, Git thường nhận ra (nhờ patch-id) và không nhân đôi, nhưng không phải lúc nào cũng vậy — dễ ra xung đột hoặc thay đổi lặp. Với việc đồng bộ nhiều commit giữa hai nhánh, merge hoặc rebase thường sạch hơn.
Đánh đổi và lưu ý
cherry-pick có thể xung đột. Diff của commit gốc dựa trên ngữ cảnh của nó; áp lên một nhánh đã khác đi có thể không khớp. Khi đó cherry-pick dừng như merge, bạn giải xung đột rồi git cherry-pick --continue (hoặc --abort).
-x để ghi nguồn. git cherry-pick -x <sha> thêm dòng (cherry picked from commit <sha>) vào message — rất đáng dùng khi bê giữa các nhánh release để sau này truy được commit gốc.
Đừng để công việc dài trong detached HEAD. Detached tốt cho xem và thử nghiệm ngắn. Nếu bắt đầu commit nhiều, tạo nhánh ngay (git switch -c) để khỏi phụ thuộc vào việc nhớ SHA và cảnh báo của Git.
Ba ý mang về
- cherry-pick bê một commit lẻ sang nhánh hiện tại bằng cách áp diff của nó và tạo commit mới — đo thật,
5aa73b3thànhaea78f7(SHA khác, cùng nội dung), main chỉ nhậnfix.txtchứ không kéo theo các commit khác của nhánh nguồn. - detached HEAD là khi HEAD trỏ thẳng vào commit (do
checkout <sha>, tag, haybisect) thay vì qua nhánh — bạn vẫn commit được, nhưng commit đó không thuộc nhánh nào; Git cảnh báo "leaving N commit behind" khi bạn rời đi. - Cứu commit trong detached HEAD bằng cách đặt tên:
git branch <tên> <sha>(hoặcgit switch -cngay tại chỗ) — detached không đáng sợ, chỉ là HEAD chưa gắn nhãn nhánh; và cherry-pick nhiều nên dè chừng commit trùng diff khác SHA.
Nguồn
- Pro Git — Git Branching: Rebasing / Distributed Git: Maintaining a Project (cherry-pick): https://git-scm.com/book/en/v2/Distributed-Git-Maintaining-a-Project
- Git Documentation — git-cherry-pick và git-checkout (mục "Detached HEAD"): https://git-scm.com/docs/git-checkout
Phần sau — bài cuối sê-ri — ta ra khỏi kho cục bộ: git fetch và git push thật sự trao đổi gì với máy chủ, pack protocol thương lượng ra sao để chỉ gửi những object còn thiếu, và vì sao đôi khi push bị từ chối.