← All posts

Git Merge, Rebase & Squash — ตอนที่ 1: กราฟ Commit และ Merge ทุกรูปแบบ

Data-Sci & Digital HealthBasic InfoTech & Computing Nexus
Git Merge, Rebase & Squash — ตอนที่ 1: กราฟ Commit และ Merge ทุกรูปแบบ
On this page

บทคัดย่อ

การจัดการประวัติการทำงานในระบบ Git อาศัยความเข้าใจพื้นฐานที่ว่าระบบไม่ได้จัดเก็บโครงการเป็นเวอร์ชันของไฟล์ แต่จัดเก็บในรูปแบบกราฟของ commit ซึ่งคำสั่งจัดการประวัติการทำงานทั้งหมดล้วนเป็นวิธีการปรับเปลี่ยนโครงสร้างกราฟดังกล่าว การดำเนินการ merge เป็นการรวมประวัติของสอง branch เข้าด้วยกันโดยปกติจะไม่เขียน commit ที่มีอยู่เดิมทิ้ง ซึ่งมีรูปแบบการทำงานหลักที่แตกต่างกันอย่างชัดเจน การทำ fast-forward merge จะเลื่อนตัวชี้ branch ไปข้างหน้าโดยไม่สร้าง merge commit ใหม่ ช่วยให้ประวัติเป็นเส้นตรงและค่า hash ไม่เปลี่ยนแปลง แต่ขอบเขตของฟีเจอร์จะหายไปจาก log ในขณะที่การทำ three-way merge จะใช้ commit ที่เป็นบรรพบุรุษร่วมกันรวมกับปลายทางของทั้งสอง branch เพื่อสร้าง merge commit ใหม่ที่มีสอง parent ซึ่งช่วยรักษาประวัติการพัฒนาที่แท้จริงและปลอดภัยสำหรับ branch ที่ใช้งานร่วมกัน แม้จะทำให้กราฟประวัติดูซับซ้อนขึ้นก็ตาม ผู้ใช้งานสามารถควบคุมการทำงานผ่าน --no-ff เพื่อบังคับให้สร้าง merge commit สำหรับรักษาขอบเขตของฟีเจอร์ให้มองเห็นได้ชัดเจน หรือใช้ --ff-only เพื่อปฏิเสธการทำ merge หากไม่สามารถเลื่อนตัวชี้ไปข้างหน้าได้อย่างสมบูรณ์แบบเพื่อรักษาประวัติให้เป็นเส้นตรง บทความนี้สร้างความเข้าใจเกี่ยวกับโมเดลของกราฟและการทำ merge ในรูปแบบต่างๆ เพื่อให้ผู้ใช้งานสามารถอ่านประวัติการแตกสายการทำงาน คาดการณ์การเกิด merge commit และเลือกพฤติกรรมการรวมโค้ดที่เหมาะสมกับความต้องการของทีมได้อย่างแม่นยำ


อินโฟกราฟิกสรุปเนื้อหา Git Merge, Rebase & Squash — ตอนที่ 1: กราฟ Commit และ Merge ทุกรูปแบบ
Visual summary · ภาพสรุป

นี่คือตอนที่ 1 ของซีรีส์ 6 ตอน ว่าด้วยเรื่องที่ว่า Git จัดการประวัติ (history) ใหม่อย่างไร ก่อนจะเปรียบเทียบ Merge, Rebase และ Squash ได้ เราต้องมี mental model หนึ่งเดียวที่ทุกอย่างตั้งอยู่บนนั้น สิ่งสำคัญที่สุดคือ:

Git ไม่ได้เก็บเป็นไฟล์เวอร์ชัน แต่เก็บเป็นกราฟของ commits

ทุกคำสั่งอย่าง Merge, Rebase, Squash คือการจัดการ กราฟประวัติ commit ต่างกัน ในตอนนี้เราจะสร้าง mental model ของ commit graph ก่อน แล้วไล่ดู Merge ทุกรูปแบบ ที่ Git ทำได้ ตั้งแต่ fast-forward merge, three-way merge ไปจนถึงสองแฟล็กที่บังคับพฤติกรรมของ Git คือ --no-ff และ --ff-only เมื่อจบตอนนี้ คุณจะอ่านกราฟประวัติที่แตก branch ได้ บอกได้ว่า merge ครั้งนั้นจะสร้าง merge commit หรือไม่ และเลือกพฤติกรรม merge ที่เหมาะกับทีมได้

Mental model: Commit graph

สมมติเริ่มจาก:

A---B---C  main

สร้าง branch ใหม่:

git switch -c feature/login

แล้วมีการพัฒนาพร้อมกันทั้งสอง branch:

ABCFGDEfeature/loginmain
ประวัติแยกสาย: feature/login (D–E) แตกออกจาก C ขณะที่ main เดินหน้าไปถึง F–G

ตอนนี้ต้องนำ feature/login กลับเข้า main ซึ่งมี 3 วิธีหลัก:

  1. Merge — เชื่อมประวัติทั้งสองสายเข้าด้วยกัน
  2. Rebase — ย้าย commits ของ feature ไปต่อท้าย main
  3. Squash — รวมหลาย commits ให้กลายเป็น commit เดียว

ในตอนนี้เราจะโฟกัสที่วิธีแรกคือ merge ส่วน rebase และ squash มีตอนของตัวเองในซีรีส์ (ดูแถบนำทางด้านบน)

Merge

แนวคิด

Merge หมายถึง:

เอาสถานะและประวัติของสอง branches มารวมกัน โดยปกติไม่เขียน commit เดิมใหม่

คำสั่งพื้นฐาน:

git switch main
git merge feature/login

Merge มีหลายรูปแบบ ซึ่งเราจะไล่ดูทีละแบบในตอนนี้

Fast-forward merge

สถานการณ์

หาก main ไม่ได้เดินหน้าต่อหลังจากสร้าง feature branch:

A---B---C  main
         \
          D---E  feature/login

เมื่อใช้:

git switch main
git merge feature/login

Git เพียงเลื่อน pointer ของ main ไปที่ E:

ABCDEmain, feature/login
Fast-forward: main เลื่อน pointer ไปข้างหน้า — ได้เส้นตรงเดียว ทั้งสอง branch ชี้ที่ E

ไม่มี merge commit เพิ่ม เราเรียกสิ่งนี้ว่า fast-forward merge

ข้อดี

ข้อเสีย

เมื่อมองจาก history ในภายหลัง อาจไม่เห็นชัดว่า commits D–E เคยเป็น feature branch เดียวกันมาก่อน

บังคับให้ merge ได้เฉพาะ fast-forward

git merge --ff-only feature/login

ถ้าต้องสร้าง merge commit คำสั่งจะหยุดแทนที่จะ merge อัตโนมัติ เหมาะสำหรับป้องกัน history ที่ไม่ตั้งใจ

Three-way merge

สถานการณ์

คราวนี้ทั้ง main และ feature มี commits ใหม่:

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

เมื่อใช้:

git switch main
git merge feature/login

Git ใช้สามจุดในการคำนวณการรวม:

  1. commit ร่วมเดิม C
  2. ปลาย branch main คือ G
  3. ปลาย branch feature คือ E

แล้วสร้าง merge commit ใหม่ M:

ABCFGDEMmain
Three-way merge: merge commit M มี parent สองตัว — G บน main และ E บน feature branch

M มี parent สองตัว:

parent 1 = G
parent 2 = E

นี่คือ three-way merge

ข้อดี

ข้อเสีย

Merge แบบ --no-ff

แม้ Git จะสามารถ fast-forward ได้ เราก็สามารถบังคับให้สร้าง merge commit ได้:

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

ก่อน merge:

A---B---C  main
         \
          D---E  feature/login

หลัง merge:

ABCDEMmain
--no-ff: บังคับสร้าง merge commit M แม้ว่าจะ fast-forward ได้

เหตุผลที่ใช้ --no-ff

เราต้องการให้ history แสดงว่า:

D และ E เป็นส่วนหนึ่งของ feature/login

เหมาะกับทีมที่ใช้แนวทางคล้าย Git Flow:

feature/*
release/*
hotfix/*

ตัวอย่าง:

git merge --no-ff feature/payment

Merge แบบ --ff-only

git merge --ff-only feature/login

ความหมาย:

Merge ได้เฉพาะกรณีที่เลื่อน pointer ไปข้างหน้าได้ โดยไม่สร้าง merge commit

ถ้า branches แยกจากกัน:

          D---E
         /
A---B---C---F---G

Git จะปฏิเสธ:

fatal: Not possible to fast-forward, aborting.

เหมาะกับทีมที่ต้องการ linear history และไม่ต้องการให้ Git สร้าง merge commit โดยไม่ตั้งใจ

Key takeaways

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

ความคิดเห็น

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

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