ข้ามไปยังเนื้อหา
Tayakorn
Git Handbook

Git Handbook

LEVEL 2 · ระดับกลาง

Merge vs Rebase

สองคำสั่งนี้ตอบโจทย์เดียวกัน — "เอางานสองสายมารวมกัน" — แต่ให้ประวัติคนละหน้าตา การเลือกใช้สะท้อนปรัชญาว่าคุณอยากให้ประวัติ บอกความจริงตามที่เกิด หรือ เล่าเรื่องให้อ่านง่าย

Merge — เก็บความจริงABCDMประวัติแตกแล้วบรรจบ — เห็นว่าเคยทำงานคู่ขนานRebase — เล่าใหม่ให้ตรงABCD'เส้นตรงเส้นเดียว — D ถูกยกมาต่อท้าย กลายเป็น D'D' = commit ใหม่ รหัสเปลี่ยน
FIG 8.1 merge เก็บร่องรอยว่าเคยแยกสาย (มี merge commit M) · rebase ยก commit ของ feature ไปวางต่อท้าย main ใหม่ ได้ประวัติเส้นตรงสะอาด แต่ D กลายเป็น D' ที่มีรหัสใหม่ (เพราะ commit แก้ไม่ได้ — บทที่ 2)

8.1 เลือกอย่างไร

MergeRebase
ประวัติตามจริง เห็นการแตกสายเส้นตรง อ่านเป็นเรื่องเดียว
commit เดิมคงไว้ทั้งหมดสร้างใหม่ (รหัสเปลี่ยน)
เหมาะกับรวมงานเข้า branch หลักที่แชร์กันเก็บกวาด branch ส่วนตัวให้สะอาดก่อนเสนอ

ทางเลือกนี้มักผูกกับกลยุทธ์ branch ของทั้งทีมด้วย — trunk-based (บทที่ 10.3) มักไปกับ rebase เพราะอยากได้ประวัติ main เป็นเส้นตรง ส่วน Git Flow ที่มี branch ยืนยาวมักไปกับ merge เพื่อเก็บร่องรอยการแตกสายไว้

8.2 interactive rebase — เครื่องมือเก็บกวาดประวัติ

git rebase -i ให้คุณแก้ชุด commit ก่อนเสนอเข้าทีม — รวมหลาย commit ย่อย ("แก้ typo", "แก้ typo อีกที") ให้เหลือก้อนเดียวที่มีความหมาย, เปลี่ยนข้อความ, สลับลำดับ คำสั่งที่ใช้บ่อย: squash (ยุบรวม), reword (แก้ข้อความ), fixup (ยุบโดยทิ้งข้อความ), drop (ลบ commit)

8.3 conflict ตอน rebase ต่างจาก merge conflict อย่างไร

merge conflict (บทที่ 10.2) เกิดครั้งเดียว แก้จบในคราวเดียว แต่ rebase เล่นซ้ำ (replay) commit ทีละดวงไปวางบนฐานใหม่ ถ้าหลาย commit ต่างชนกับฐานคนละจุด conflict จะโผล่ทีละรอบตามลำดับ ไม่ใช่ครั้งเดียวจบ:

$ git rebase main
CONFLICT (content): Merge conflict in price.js
# แก้ไฟล์ ลบเครื่องหมาย <<< === >>> เหมือน merge conflict ทุกประการ
$ git add price.js
$ git rebase --continue     # ไปต่อ commit ถัดไป — อาจเจอ conflict ซ้ำอีกดวง

ทางเลือกเมื่อติด conflict ระหว่าง rebase: --continue แก้เสร็จแล้วไปต่อ, --skip ข้าม commit นี้ทั้งดวง (ทิ้งการเปลี่ยนแปลงของมันไปเลย), --abort ยกเลิกทั้งหมดกลับไปจุดก่อน rebase เหมือนไม่เคยเริ่ม — ตัวเลือกที่ปลอดภัยที่สุดเวลาพันกันจนไม่แน่ใจ หาก conflict เดิมโผล่ซ้ำหลายรอบระหว่าง rebase ยาว ๆ นั่นคือสถานการณ์ที่ rerere (บทที่ 13) ถูกออกแบบมาแก้โดยเฉพาะ

สรุปบทที่ 8 — merge เก็บประวัติตามจริง (มี merge commit), rebase ทำประวัติให้เป็นเส้นตรงโดยสร้าง commit ใหม่ · ใช้ rebase เก็บกวาด branch ส่วนตัว, ใช้ merge รวมเข้า branch หลัก · rebase -i ยุบ/แก้/จัดเรียง commit ก่อนเสนอ · conflict ตอน rebase แก้ไฟล์เหมือน merge แต่เกิดซ้ำได้ทีละ commit ใช้ --continue/--skip/--abort · กฎเหล็ก: ห้าม rebase สิ่งที่แชร์ไปแล้ว

อ่านแบบเต็มเล่ม