จัดระเบียบ Git History ให้อ่านง่ายด้วย git rebase (และกฎเหล็กที่ห้ามลืม!)

บทความนี้มาจากที่ผมอยากแนะนำ Front-end มือใหม่ให้รู้จักกับ Git Workflow เพื่อนำไปประยุกต์ใช้ในการทำงานจริง เลยตั้งใจจะเขียนเป็นซีรีส์ "Git Workflow สำหรับ Front-End Dev จาก Zero สู่ Hero ในทีม" และนี่คือบทความสุดท้ายครับ


ตลอดสามบทความที่ผ่านมา เราได้เดินทางจากการใช้ Git พื้นฐาน, เรียนรู้การแตก Branch เพื่อทำงานร่วมกันอย่างปลอดภัย, ไปจนถึงการรับมือกับ Merge Conflict อย่างมืออาชีพ ตอนนี้ Workflow การทำงานของคุณถือว่าสมบูรณ์และใช้งานได้จริงแล้ว

แต่...เคยลองใช้คำสั่ง git log --oneline --graph ดูประวัติโปรเจกต์ของคุณไหม? ถ้าโปรเจกต์มีการ Merge บ่อยๆ คุณอาจจะเห็นเส้นกราฟที่แตกกิ่งก้านสาขาและพันกันยุ่งเหยิงเหมือนใยแมงมุม ประวัติศาสตร์เหล่านี้แม้จะ "ถูกต้อง" ตามสิ่งที่เกิดขึ้นจริง แต่มัน "อ่านยาก" และยากที่จะไล่ทำความเข้าใจ

นี่คือจุดที่ "ช่างฝีมือ" (Craftsmanship) เข้ามามีบทบาทครับ เราไม่ได้ต้องการแค่โค้ดที่ทำงานได้ แต่เราต้องการประวัติศาสตร์ของโปรเจกต์ที่สะอาด เป็นเส้นตรง และเล่าเรื่องราวได้อย่างชัดเจน และเครื่องมือที่จะช่วยเราในเรื่องนี้ก็คือ git rebase

rebase คืออะไร? ต่างจาก merge อย่างไร?

เพื่อให้เข้าใจง่ายที่สุด ให้คิดว่า merge กับ rebase คือวิธีการรวมโค้ดสองแบบที่มีปรัชญาต่างกัน

  • git merge (การรวมแบบตรงไปตรงมา): ปรัชญาของ merge คือ "การบันทึกประวัติศาสตร์ตามความเป็นจริง" มันจะนำ Branch หนึ่งมารวมกับอีก Branch หนึ่ง แล้วสร้าง "Merge Commit" ขึ้นมาใหม่เพื่อเป็นจุดเชื่อมต่อ ผลลัพธ์คือประวัติศาสตร์ที่ซื่อตรง แต่ก็อาจจะซับซ้อนและยุ่งเหยิง
  • git rebase (การย้ายฐานและเรียงใหม่): ปรัชญาของ rebase คือ "การเขียนประวัติศาสตร์ใหม่เพื่อความสวยงาม" แทนที่จะสร้างจุดเชื่อมต่อ rebase จะยก Commit ทั้งหมดใน Branch ของคุณไปพักไว้ก่อน จากนั้นมันจะย้าย "ฐาน" (Base) ของ Branch คุณไปต่อท้ายสุดของ Branch หลัก แล้วจึงนำ Commit ของคุณมาวางเรียงต่อทีละอัน ผลลัพธ์คือประวัติศาสตร์ที่เป็นเส้นตรง สวยงาม เหมือนกับว่าคุณเพิ่งจะเริ่มทำงานหลังจากที่ทุกคนใน Branch หลักทำเสร็จแล้ว

พูดง่ายๆ คือ merge ทำให้ History เป็นกิ่งก้าน แต่ rebase ทำให้ History เป็นเส้นตรง

The Golden Rule กฎเหล็กที่ห้ามฝ่าฝืนเด็ดขาด!

ก่อนที่เราจะไปต่อ มีกฎที่สำคัญที่สุดข้อหนึ่งที่คุณต้องจำให้ขึ้นใจและห้ามทำเด็ดขาด นี่คือกฎทองของ Rebase ครับ

ห้ามใช้ rebase กับ Branch สาธารณะที่คนอื่นใช้งานร่วมกับคุณเด็ดขาด (เช่น main, develop, หรือ Branch ที่เพื่อนร่วมทีมคนอื่นดึงไปใช้แล้ว)

ทำไม? เพราะ rebase คือการ "เขียนประวัติศาสตร์ใหม่" ซึ่งหมายความว่ามันจะลบ Commit เก่าทิ้ง แล้วสร้าง Commit ใหม่ขึ้นมาแทนที่ ซึ่ง Commit ใหม่เหล่านี้จะมี ID (Hash) ที่ไม่เหมือนเดิม หากคุณ rebase บน Branch ที่คนอื่นใช้อยู่ ประวัติศาสตร์ของคุณกับของเพื่อนร่วมทีมจะขัดแย้งกันทันที และเมื่อพวกเขาพยายาม pull หรือ push มันจะก่อให้เกิดความโกลาหลที่แก้ไขได้ยากมาก

ดังนั้น จำไว้ว่า: Rebase ใช้สำหรับจัดระเบียบ Branch ส่วนตัวของคุณ ที่ยังไม่มีใครคนอื่นเอาไปใช้เท่านั้น

เทคนิคทำ History ให้คลีนด้วย Interactive Rebase

พลังที่แท้จริงของ rebase ที่เรามักใช้กันบ่อยๆ คือการจัดระเบียบ Commit ของตัวเองใน Feature Branch ก่อน ที่จะนำไปรวมกับ Branch หลัก ผ่านโหมดที่เรียกว่า "Interactive Rebase" (rebase -i)

ลองนึกภาพว่าใน Feature Branch ของคุณ คุณมี Commit ย่อยๆ เต็มไปหมด

  1. commit 1: feat: add user profile page structure
  2. commit 2: fix: correct typo in title
  3. commit 3: style: add basic styling for profile card
  4. commit 4: fix: oops forgot to add padding

ก่อนที่คุณจะสร้าง Pull Request เพื่อให้คนอื่นรีวิว คุณคงไม่อยากให้พวกเขานั่งอ่าน Commit ยิบย่อย 4 อันนี้ใช่ไหมครับ? เราสามารถรวมมันให้เหลือแค่ Commit เดียวที่สมบูรณ์ได้

ขั้นตอน

  1. สมมติว่าคุณต้องการรวม 4 Commit ล่าสุด ให้รันคำสั่ง
git rebase -i HEAD~4
  1. คำสั่งนี้จะเปิด Text Editor ขึ้นมา พร้อมกับลิสต์ของ Commit ทั้ง 4 และคำสั่งที่คุณสามารถใช้ได้
pick a1b2c3d feat: add user profile page structure
pick e4f5g6h fix: correct typo in title
pick i7j8k9l style: add basic styling for profile card
pick m0n1o2p fix: oops forgot to add padding

# Commands:
# p, pick = use commit
# s, squash = use commit, but meld into previous commit
  1. เราต้องการเก็บ Commit แรก (pick) ไว้ และรวม Commit ที่เหลือทั้ง 3 อันเข้าไปใน Commit แรก ดังนั้นให้เราเปลี่ยนคำสั่ง pick ของ Commit ที่ 2, 3, 4 เป็น squash (หรือ s)
pick a1b2c3d feat: add user profile page structure
s e4f5g6h fix: correct typo in title
s i7j8k9l style: add basic styling for profile card
s m0n1o2p fix: oops forgot to add padding
  1. บันทึกและปิดไฟล์นั้น Git จะเปิด Editor ขึ้นมาอีกครั้งเพื่อให้คุณเขียน Commit Message ใหม่สำหรับ Commit ที่ถูกรวมกันแล้วทั้งหมด คุณก็จัดการเขียนข้อความที่สรุปทุกอย่างให้สมบูรณ์ เช่น feat: create user profile page with styling

เมื่อทำเสร็จ ประวัติศาสตร์ใน Branch ของคุณจะเหลือเพียง Commit เดียวที่สะอาดและสมบูรณ์ พร้อมสำหรับให้ทีมรีวิว

บทสรุป และส่งท้ายซีรีส์

git rebase คือเครื่องมือที่ทรงพลังสำหรับนักพัฒนาที่ใส่ใจในรายละเอียด มันช่วยยกระดับจากการทำงานที่ "แค่เสร็จ" ไปสู่การทำงานที่มี "คุณภาพและความเป็นมืออาชีพ" การมี Git History ที่สะอาดไม่ได้เป็นแค่เรื่องของความสวยงาม แต่มันช่วยให้การไล่หาบั๊ก (Debug) และการทำความเข้าใจโปรเจกต์ในอนาคตง่ายขึ้นอย่างมหาศาล

ความแตกต่างระหว่าง Git rebase และ Git merge
Git คือระบบควบคุมเวอร์ชันของโค็ดที่ทรงพลังและยืดหยุ่น ช่วยให้นักพัฒนาสามารถทำงานร่วมกันในแต่ละโปรเจ็กต์ได้อย่างมีประสิทธิภาพ โดยคุณสมบัติที่สำคัญ 2 อย่างใน Git คือคำสั่ง rebase และ merge การทำงานของทั้งสองตัวช่วยในการเปลี่ยนแปลงโค็ดจาก Branch หนึ

และนี่ก็คือบทสรุปของซีรีส์ "Git Workflow สำหรับ Front-End Dev" เราได้เดินทางตั้งแต่การทำความเข้าใจพื้นฐานที่แท้จริงของ Git (commit), เรียนรู้การทำงานร่วมกับผู้อื่นอย่างปลอดภัย (branch), เผชิญหน้ากับปัญหาที่หลีกเลี่ยงไม่ได้ (merge conflict), และปิดท้ายด้วยการขัดเกลาผลงานของเราให้เฉียบคม (rebase)

หวังว่าซีรีส์นี้จะช่วยขจัดความกลัวและทำให้ Git กลายเป็นเพื่อนซี้ที่ไว้ใจได้ของคุณนะครับ แน่นอนว่าโลกของ Git ยังมีอะไรให้เรียนรู้อีกมากมาย แต่ผมเชื่อว่ารากฐานที่คุณได้จากที่นี่เพียงพอที่จะทำให้คุณทำงานในทีมได้อย่างมั่นใจและเป็นมืออาชีพแล้ว การเดินทางของคุณกับ Git เพิ่งจะเริ่มต้นขึ้น ขอให้สนุกกับการเขียนโค้ดครับ!