← All posts

Git Merge, Rebase & Squash — ตอนที่ 3: Rebase — เขียนประวัติใหม่อย่างปลอดภัย

Data-Sci & Digital HealthBasic InfoTech & Computing Nexus
Git Merge, Rebase & Squash — ตอนที่ 3: Rebase — เขียนประวัติใหม่อย่างปลอดภัย
On this page

บทคัดย่อ

การบริหารจัดการประวัติการทำงานในระบบควบคุมเวอร์ชันให้มีความเป็นระเบียบมีความสำคัญอย่างยิ่งต่อการพัฒนาซอฟต์แวร์ร่วมกัน ในขณะที่การทำ merge จะเป็นการเชื่อมประวัติการทำงานโดยรักษารูปแบบกราฟสายการพัฒนาเดิมไว้ และการทำ squash จะรวบรวมหลาย commit ให้กลายเป็นหนึ่งเดียว กลไกของ rebase กลับใช้วิธีการเขียนประวัติการทำงานใหม่ทั้งหมดซึ่งทรงพลังแต่ต้องใช้ความระมัดระวังสูง กระบวนการ rebase ทำงานโดยการนำ commit จาก branch ปัจจุบันไปสร้างขึ้นใหม่ทีละรายการบนฐานใหม่ ส่งผลให้ประวัติการทำงานถูกจัดเรียงเป็นเส้นตรงและทำให้สามารถทำ fast-forward ได้โดยไม่ก่อให้เกิด merge commit ทว่าการเปลี่ยน parent commit ในกระบวนการนี้จะทำให้ค่า hash ของแต่ละ commit เปลี่ยนแปลงไปด้วยเสมอ ดังนั้น กฎทองที่สำคัญคือห้ามทำ rebase บน branch สาธารณะหรือที่ใช้งานร่วมกัน เช่น main, master หรือ production โดยเด็ดขาด แต่ควรจำกัดขอบเขตการใช้งานไว้เฉพาะบน private feature branch หรือ local commit ที่ยังไม่ได้เผยแพร่เท่านั้น นอกจากนี้ เมื่อจำเป็นต้องอัปเดตข้อมูลไปยังเซิร์ฟเวอร์หลังการทำ rebase จะต้องใช้แฟล็ก --force-with-lease แทนการใช้ --force แบบปกติ เพื่อตรวจสอบและป้องกันการเขียนทับผลงานของเพื่อนร่วมทีมโดยไม่ตั้งใจ บทความนี้อธิบายกลไกที่อยู่เบื้องหลังการทำ rebase เปรียบเทียบความแตกต่างกับการทำงานรูปแบบอื่น พร้อมทั้งกำหนดข้อควรระวังเพื่อให้ทีมพัฒนาสามารถปรับแต่งประวัติของโครงการได้อย่างปลอดภัย


อินโฟกราฟิกสรุปเนื้อหา Git Merge, Rebase & Squash — ตอนที่ 3: Rebase — เขียนประวัติใหม่อย่างปลอดภัย
Visual summary · ภาพสรุป

ในตอนที่ 1 และ 2 เราเห็นแล้วว่า Merge เชื่อมประวัติสองสายเข้าด้วยกัน และ Squash รวมหลาย commit ให้เหลือ commit เดียว ตอนที่ 3 นี้เราจะพูดถึง Rebase ซึ่งเป็นเครื่องมือที่ทรงพลังที่สุดและต้องระวังที่สุดในสามตัว เพราะมันไม่ได้แค่เชื่อมประวัติ แต่ เขียนประวัติใหม่ (rewrite history)

เมื่ออ่านจบตอนนี้ คุณจะเข้าใจว่า rebase ทำงานอย่างไรในระดับกราฟ commit ทำไม commit hash ถึงเปลี่ยน rebase ต่างจาก merge อย่างไร และที่สำคัญที่สุดคือ กฎความปลอดภัย ว่า branch ไหน rebase ได้และ branch ไหนห้ามแตะ พร้อมวิธี rebase feature branch ให้ทัน main อย่างถูกต้องด้วย --force-with-lease

Rebase คืออะไร — ย้าย commit ไปสร้างใหม่บนฐานใหม่

Rebase หมายถึง:

นำ commits ของ branch ปัจจุบันไปสร้างใหม่บน base ใหม่

สมมติเราอยู่ในสถานการณ์ที่ทั้ง feature และ main เดินหน้าไปคนละทาง:

          D---E  feature/login
         /
A---B---C---F---G  main

ขณะที่เราทำ D และ E อยู่บน feature/login ทาง main ก็ได้ F และ G เพิ่มเข้ามา ถ้าอยากให้ feature ของเราตั้งอยู่บนงานล่าสุดของ main โดยไม่สร้าง merge commit เราใช้ rebase โดยยืนอยู่ที่ feature branch:

git switch feature/login
git rebase main

Git จะนำการเปลี่ยนแปลงของ D และ E ไปสร้างเป็น commit ใหม่ต่อท้าย G ทีละตัว:

ABCFGD′E′DEfeature/loginabandoned
Rebase สร้าง D′ และ E′ เป็น commit ใหม่ต่อจาก G ส่วน D และ E เดิมถูกทิ้งไป

จากนั้นก็นำ feature เข้า main ได้แบบ fast-forward เพราะตอนนี้ feature ต่อยอดตรงจากปลายของ main พอดี:

git switch main
git merge --ff-only feature/login

ผลลัพธ์:

A---B---C---F---G---D'---E'  main

History กลายเป็น เส้นตรง ไม่มีเส้นแตกแขนงและไม่มี merge commit

ทำไม commit เปลี่ยนจาก D เป็น D′

จุดที่หลายคนสับสนคือ ทำไมหลัง rebase แล้ว commit ถึงกลายเป็น D′ และ E′ ไม่ใช่ D และ E ตัวเดิม คำตอบอยู่ที่วิธีที่ Git คำนวณ commit hash

Commit hash คำนวณจากหลายอย่างประกอบกัน เช่น:

เมื่อ rebase ทำให้ parent ของ D เปลี่ยนจาก C ไปเป็น G:

เดิม: parent(D)  = C
ใหม่: parent(D') = G

เมื่อ parent เปลี่ยน หนึ่งในวัตถุดิบของ hash ก็เปลี่ยน ดังนั้น hash จึงต้องเปลี่ยนตามไปด้วย:

D ≠ D'
E ≠ E'

นี่คือเหตุผลที่ rebase ถือเป็นการ rewrite history — Git ไม่ได้ย้าย commit เดิมไปวางที่ใหม่ แต่สร้าง commit ชุดใหม่ที่มีเนื้อหาเดียวกันขึ้นมาแทน ส่วน commit เดิม (D, E) จะกลายเป็น commit ที่ไม่มี branch ชี้ถึงและถูกเก็บกวาดออกไปในภายหลัง

Merge vs Rebase

ทั้ง merge และ rebase ต่างก็แก้ปัญหาเดียวกัน คือ "จะนำงานสองสายมารวมกันอย่างไร" แต่ให้กราฟที่ต่างกันโดยสิ้นเชิง

Merge — เก็บกราฟจริง

Merge สร้าง merge commit และคงรูปการแตกแขนงของประวัติไว้:

ABCFGDEM
Merge คงรูปการแตกแขนงไว้: D–E รวมกลับเข้า main ผ่าน merge commit M ที่มี parent สองตัว

ยังเห็นชัดว่า D และ E เคยเป็น feature branch แยกออกไป แล้วถูกรวมกลับที่ M

Rebase — ทำให้เป็นเส้นตรง

Rebase ย้าย commit ให้ไปเรียงต่อกันเป็นเส้นเดียว ไม่มี merge commit:

ABCFGD′E′
Rebase ทำให้ประวัติกลายเป็นเส้นตรงเดียว ไม่มี merge commit

ตารางเปรียบเทียบ

ประเด็น Merge Rebase
เก็บ commit เดิม ใช่ ไม่ใช่
Commit hash ไม่เปลี่ยน เปลี่ยน
History แตกแขนง เส้นตรง
สร้าง merge commit อาจสร้าง ไม่สร้างจาก rebase
เหมาะกับ shared branch ใช่ ต้องระวัง
Conflict มักแก้ครั้งเดียว อาจแก้ทีละ commit
เห็นประวัติ feature ชัด ไม่ชัดเท่า merge

กฎสำคัญที่สุดของ Rebase

อย่า rebase commits ที่คนอื่นนำไปใช้งานแล้ว เว้นแต่ทีมตกลงกัน

เพราะ rebase สร้าง commit hash ชุดใหม่ทับของเดิม ถ้ามีคนอื่นอ้างอิงหรือแตก branch ต่อจาก commit เดิมอยู่ ประวัติของเขาจะไม่ตรงกับของเราทันที ตัวอย่างที่ ไม่ควรทำ:

git switch main
git rebase some-branch
git push --force

ถ้า main เป็น public/shared branch การทำแบบนี้จะทำให้ประวัติของทุกคนในทีมไม่ตรงกัน และกู้คืนได้ยาก

โดยทั่วไป rebase ได้อย่างปลอดภัยกับ

โดยทั่วไปไม่ควร rebase

Rebase feature branch ให้ทัน main

นี่เป็น use case ที่ใช้บ่อยที่สุดของ rebase คือ การอัปเดต feature branch ของเราให้ตั้งอยู่บนงานล่าสุดของ main ก่อนเปิดหรือ merge PR ขั้นตอนคือ อัปเดต main ให้เป็นล่าสุดก่อน แล้วค่อย rebase feature ทับ:

git switch main
git pull

git switch feature/login
git rebase main

เมื่อ rebase เสร็จ commit hash ของ feature เปลี่ยนไปแล้ว ถ้าเคย push branch นี้ขึ้น remote มาก่อน การ push ครั้งนี้จะไม่ใช่ fast-forward จึงต้องเขียนทับ แต่ต้องเขียนทับแบบปลอดภัย:

git push --force-with-lease

ไม่ควรใช้:

git push --force

ความต่างสำคัญมาก: --force-with-lease จะตรวจก่อนว่าบน remote มีงานใหม่ของคนอื่นที่เรายังไม่เห็นหรือไม่ ถ้ามี มันจะปฏิเสธการ push เพื่อไม่ให้เราเผลอเขียนทับงานของคนอื่น ส่วน --force ธรรมดาจะเขียนทับทันทีโดยไม่ตรวจอะไรเลย ซึ่งเสี่ยงทำงานของเพื่อนหายแบบเงียบ ๆ ดังนั้นบน branch ที่เคย push แล้วให้ใช้ --force-with-lease เป็นค่าเริ่มต้นเสมอ

Key takeaways

0
ถึงนักอ่านชาวไทยและต่างชาติทำความเข้าใจบริบททางการแพทย์ของผมอ่านต่อ →ถึงนักอ่านชาวไทยและต่างชาติทำความเข้าใจเนื้อหาของผมที่นอกเหนือจากการแพทย์อ่านต่อ →

ความคิดเห็น

ยังไม่มีความคิดเห็น มาเป็นคนแรกกันเลย

เข้าสู่ระบบเพื่อแสดงความคิดเห็น