กลับไปที่บล็อก

Git cherry-pick: หลายคอมมิต หลายบรานช์ และคอนฟลิกต์

12 กันยายน 2569

TL;DR: git cherry-pick <sha> คัดลอกคอมมิตหนึ่งตัวจากบรานช์ใดก็ได้มาไว้บนบรานช์ปัจจุบัน — แพตช์เดิม SHA ใหม่ ไม่มีประวัติใดขยับ สำหรับหลายคอมมิต ให้พิมพ์รายชื่อ SHA หรือใช้ช่วง (git cherry-pick A..B); ข้อควรจำคือช่วงนี้ไม่รวม A ให้เขียน A^..B เพื่อรวมมัน ถ้าหยุดที่คอนฟลิกต์ ให้แก้แล้ว git add แล้ว git cherry-pick --continue cherry-pick ใช้เพื่อย้ายแพตช์เฉพาะเจาะจง — ถ้าคุณต้องการทุกอย่างจากอีกบรานช์ ให้ใช้ merge หรือ rebase แทน

git cherry-pick ทำอะไรกันแน่?

cherry-pick หยิบคอมมิตที่มีอยู่แล้วและเอา diff ของมันมาเป็นคอมมิตใหม่บนบรานช์ปัจจุบันของคุณ คอมมิตต้นฉบับอยู่ที่เดิม สำเนาได้ SHA ใหม่สด ๆ git ไม่ได้ “ย้าย” อะไรเลย — คนที่ภายหลังงงว่าทำไมคอมมิตยังโผล่บนบรานช์เก่ากำลังเห็นข้อเท็จจริงนี้พอดี

โมเดลคัดลอก-ไม่ใช่ย้ายนี้ตัดสินว่า cherry-pick เป็นเครื่องมือที่ใช่เมื่อไร:

  • ต้องการหนึ่งการแก้จาก feature branch มาที่ main ตอนนี้ โดยไม่ merge ที่เหลือ
  • hotfix ที่ถูก commit ผิดบรานช์ต้องมาลงบรานช์ที่ถูก
  • แพตช์ต้องเล่นซ้ำลง release branch ที่ไม่เคย merge จาก main

ทักษะคู่กันคือรู้วิธีถอยคอมมิตที่ปรากฏว่าผิด — กลไกของเรื่องนี้อยู่ใน git undo last commit: เก็บการเปลี่ยนแปลงไว้

cherry-pick คอมมิตจากบรานช์อื่นอย่างไร?

หา SHA สลับไปบรานช์เป้าหมาย แล้วหยิบ:

# 1. หาตำแหน่งคอมมิตบนบรานช์ต้นทาง
git log feature/payment-fix --oneline -5

# 2. สลับไปบรานช์ที่จะรับมัน
git switch main

# 3. คัดลอกมา
git cherry-pick 1a2b3c4

สองความสะดวกที่ควรรู้:

  • git cherry-pick <branch> หยิบคอมมิตปลายสุดของบรานช์นั้น — สะดวก แต่กดผ่านโดยบังเอิญง่าย เมื่อความเข้าใจเรื่อง “ปลายสุด” ล้าสมัย
  • หลังหยิบ git log -1 --stat ยืนยันว่าอะไรลงมาแล้ว หนึ่งวินาทีของการอ่านช่วยเซฟการ revert ได้

คอมมิตยังอยู่บน feature/payment-fix ลบบรานช์นั้นเมื่อไรก็ได้ — สำเนาที่ถูกหยิบมาบน main มี SHA ของตัวเองและไม่พึ่งพาตัวเก่า

cherry-pick หลายคอมมิตอย่างไร?

สามรูปแบบ เรียงตามความถี่ที่คุณต้องใช้:

# 1. ระบุชื่อเอง — ถูกหยิบตามลำดับที่พิมพ์
git cherry-pick 1a2b3c4 5d6e7f8

# 2. ช่วง — ทุกอย่างหลัง A ถึงรวม B
git cherry-pick A..B

# 3. ช่วงที่รวม A
git cherry-pick A^..B

ความต่างระหว่าง A..B กับ A^..B คือเรื่องน่าตกใจสุดคลาสสิก: A..B ตัด A ทิ้ง ถ้าคุณนึกช่วงจาก git log แล้วหยิบ oldest..newest คุณจะข้ามคอมมิตเก่าที่สุดไปแบบเงียบ ๆ เมื่อเป้าหมายคือ “คอมมิตเก่า ๆ หลายตัวตามลำดับ” ให้เขียน oldest^..newest แล้ว off-by-one จะหายไป

ที่จะรวมหลายคอมมิตที่หยิบมาเป็นหนึ่งเดียว ใช้ stage โดยไม่ commit ผ่าน -n / --no-commit แล้ว commit ครั้งเดียว:

git cherry-pick -n 1a2b3c4 5d6e7f8
git commit -m "Backport: payment retry fixes"

ทำไม git cherry-pick ถึงไม่ทำงาน?

สี่สาเหตุจริง เรียงตามความถี่:

1. คอนฟลิกต์หยุดการหยิบ git ใช้แพตช์ เจอบรรทัดที่เปลี่ยนทั้งสองบรานช์ แล้วหยุดกลางทาง:

# แก้ไฟล์เสร็จแล้ว:
git add <resolved-files>
git cherry-pick --continue   # หรือ --abort เพื่อกลับไปก่อนเริ่มหยิบ

--continue ไม่ใช่สิ่งที่เลือกได้หรือถือว่าหมายถึงกัน — จนกว่าคุณจะรันมัน คุณอยู่ในลำดับ cherry-pick ที่พักค้างและ git status จะย้ำอย่างนั้นต่อไป

2. การหยิบว่างเปล่า (“The previous cherry-pick is now empty”) การเปลี่ยนแปลงนี้มีอยู่บนบรานช์นี้แล้ว มักจากการหยิบรอบก่อนหรือ merge ที่ถูก squash ข้ามด้วย git cherry-pick --skip หรือบังคับคอมมิตว่างด้วย --allow-empty ถ้าคุณจริง ๆ ต้องการหมุดนั้น

3. คอมมิตนั้นเป็น merge commit การ merge มีพ่อสองตัว “ใช้ diff นี้” จึงกำกวม — git ปฏิเสธแทนการเดา บอกว่าเทียบกับพ่อตัวไหน:

git cherry-pick -m 1 <merge-sha>   # พ่อ 1 = บรานช์ที่ merge*เข้ามา*

4. working tree ผิดหรือ HEAD หลุด การหยิบจะลงตรงที่ HEAD ชี้ เช็ก git branch --show-current ก่อนหยิบ ถ้าไม่พิมพ์อะไรออกมา คุณอยู่ใน detached HEAD และคอมมิตจะกลายเป็นของกำพร้าเมื่อคุณสลับที่

Cherry-pick vs merge vs rebase: ใช้ตัวไหนเมื่อไร?

คำสั่งสิ่งที่ลงบนเป้าหมายประวัติใช้เมื่อ
git cherry-pick <sha>เฉพาะคอมมิตที่ระบุคัดลอก SHA ใหม่แพตช์เจาะจงต้องย้ายตอนนี้
git merge <branch>ทุกอย่างบนบรานช์merge commit หรือ fast-forwardต้องการทั้งบรานช์ เห็นความแยกชัด
git rebase <base>ทุกคอมมิตบนบรานช์ เล่นซ้ำเป็นเส้นตรง SHA ถูกเขียนใหม่ต้องการบรานช์เป็นแถวเส้นตรงสะอาด
git revert <sha>คอมมิตกลับด้านเพิ่ม undo commitคอมมิตที่ลงแล้วต้องถูกยกเลิกบนประวัติร่วม

กฎหนึ่งบรรทัด: cherry-pick ย้ายสิ่งที่เลือก; merge กับ rebase ย้ายทุกอย่าง ยื่นมือไปหา cherry-pick เพื่อ “ซิงก์” กับบรานช์คือสัญญาณว่าจริง ๆ คุณต้องการ merge — และถ้าบรานช์นั้นคือ main ของ fork กับ upstream ขั้นตอนเต็มอยู่ที่ซิงก์ fork กับ upstream ทีละขั้น

นิสัยที่ควรข้าม: cherry-pick คอมมิตเดิมเข้าหลายบรานช์ในระยะยาว ทุกการแก้ในอนาคตบนบรานช์ต้นทางต้องหยิบใหม่อีก และบรานช์จะลอยแยกกันในที่สุด การ backport ลง release branch เป็นรูปแบบปกติ แต่จักรวาลขนานถาวรไม่ใช่

เวิร์กโฟลว์ cherry-pick ฉบับย่อ

git log <source-branch> --oneline -5   # หา SHA
git switch <target-branch>             # ลงจอดที่ถูกที่
git cherry-pick A^..B                  # ช่วง รายชื่อ หรือ SHA เดียว
# เจอคอนฟลิกต์: แก้ → git add → git cherry-pick --continue
git log -1 --stat                      # ยืนยันว่าอะไรลงมา

หา สลับ หยิบ ตรวจ คำสั่งนี้มีชื่อเสียงเรื่องอันตรายที่มันไม่สมควรได้รับ — diff อย่างใดอย่างหนึ่งจะถูกใช้ได้ หรือมันหยุดและบอกคุณว่าทำไม ความผิดพลาดที่ทำลายข้อมูลจริงมีอย่างเดียวคือหยิบผิดบรานช์ และ git log -1 ก่อนพุชทำให้พลาดจุดนั้นยาก

— mrsaynothing

— mrsaynothing

บันทึกหน้างานเรื่อง AI, Linux และ self-hosted

คุยต่อโพสต์นี้บน dev.to dev.to ↗

รับวิธีแก้ฉบับถัดไปทางอีเมล

อีเมลหนึ่งฉบับต่อหนึ่งโพสต์ แก้เสร็จแล้วไปต่อ

self-hosted · ไม่มีบุคคลที่สาม · ยกเลิกได้ในคลิกเดียว

นี่คืออะไร?

Cron vs systemd timers: ควรใช้แบบไหน

ถ้าอ่านแล้วชอบ — ผมสร้างงานแบบนี้เป็นอาชีพ จ้างผม