เปรียบเทียบ

Git Flow, GitHub Flow และ trunk-based: ควรเลือก Git workflow แบบไหน

อัปเดต: · Skillaria.Top

Git Flow คือโมเดลการแตกบรานช์ที่มี main และ develop เป็นบรานช์ถาวร และมีบรานช์ชั่วคราว feature, release และ hotfix เหมาะกับผลิตภัณฑ์ที่มีเวอร์ชันตามแผน สำหรับเว็บเซอร์วิสที่ดีพลอยบ่อย GitHub Flow (บรานช์สั้นจาก main และ pull request) หรือ trunk-based development ที่ใช้ feature flags มักเรียบง่ายกว่า

Git workflow คืออะไร และทำไมการเลือกจึงสำคัญ

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

โมเดลที่รู้จักมากที่สุดคือ Git Flow, GitHub Flow (รูปแบบหนึ่งของ Feature Branch Workflow) และ trunk-based development ไม่ใช่ "แบบเก่ากับแบบใหม่" แต่เป็นเครื่องมือสำหรับเงื่อนไขต่างกัน ได้แก่ คุณปล่อยรีลีสบ่อยแค่ไหน คุณดูแลเวอร์ชันของผลิตภัณฑ์พร้อมกันกี่เวอร์ชัน และการทดสอบอัตโนมัติของคุณพัฒนาดีแค่ไหน

Git Flow: โมเดลที่มีบรานช์ develop และ release

Git Flow ถูกอธิบายโดย Vincent Driessen ในปี 2010 โมเดลสร้างขึ้นจากบรานช์ถาวรสองตัว:

  • main (ในโปรเจกต์เก่าเรียก master) มีเฉพาะโค้ดที่ปล่อยแล้ว ทุกคอมมิตที่นี่สอดคล้องกับรีลีสและถูกทำเครื่องหมายด้วยแท็ก
  • develop บรานช์รวมงาน ที่ฟีเจอร์ที่พร้อมแล้วถูก merge เข้าสำหรับรีลีสถัดไป

และบรานช์ชั่วคราวสามประเภท:

บรานช์สร้างจากmerge ไปที่วัตถุประสงค์
feature/*developdevelopพัฒนาฟังก์ชันใหม่
release/*developmain และ developทำให้เวอร์ชันเสถียร: แก้บั๊ก หมายเลขเวอร์ชัน เอกสาร
hotfix/*mainmain และ developแก้ไขด่วนในเวอร์ชันที่ปล่อยแล้ว

Git Flow ด้วย Git ล้วน ๆ

วงจรของฟีเจอร์หนึ่งและรีลีสหนึ่งมีหน้าตาดังนี้:

# ฟีเจอร์
git switch develop
git switch -c feature/cart-discounts
# ... คอมมิต ...
git switch develop
git merge --no-ff feature/cart-discounts
git branch -d feature/cart-discounts

# รีลีส
git switch -c release/1.4.0 develop
# ... แก้เวอร์ชันและบักฟิกซ์ ...
git switch main
git merge --no-ff release/1.4.0
git tag -a v1.4.0 -m "Release 1.4.0"
git switch develop
git merge --no-ff release/1.4.0
git branch -d release/1.4.0

แฟล็ก --no-ff สำคัญมากสำหรับโมเดลนี้: มันสร้างคอมมิต merge แม้ว่าจะเลื่อนไปข้างหน้าได้ ทำให้ประวัติยังเห็นว่าคอมมิตใดอยู่ในฟีเจอร์หรือรีลีสเดียวกัน

ส่วนขยาย git-flow

เพื่อไม่ต้องพิมพ์ลำดับคำสั่งเหล่านี้ด้วยมือ มียูทิลิตี git-flow (ปัจจุบันมักติดตั้งฟอร์กที่ยังดูแลอยู่ ซึ่งในตัวจัดการแพ็กเกจมักชื่อ git-flow-avh) ยูทิลิตีนี้เพิ่มคำสั่งย่อย:

git flow init -d
git flow feature start cart-discounts
git flow feature finish cart-discounts
git flow release start 1.4.0
git flow release finish 1.4.0
git flow hotfix start 1.4.1
git flow hotfix finish 1.4.1

git flow init ถามชื่อบรานช์และ prefix (ด้วย -d จะรับค่าเริ่มต้น) ส่วนคำสั่ง finish ทำการ merge ทั้งหมดที่จำเป็น ติดแท็ก และลบบรานช์ชั่วคราว GUI ไคลเอนต์หลายตัวก็ทำงานตาม Git Flow ผ่านปุ่มได้เช่นกัน

ข้อดีและข้อเสียของ Git Flow

ข้อดี: โครงสร้างชัดเจน สถานะของโค้ดเข้าใจง่าย (main คือสิ่งที่ลูกค้าใช้ develop คือเวอร์ชันถัดไป) มีช่วงเวลาทำให้รีลีสเสถียรแยกต่างหาก และการดูแล hotfix สะดวก โมเดลนี้เหมาะกับแอปมือถือ ซอฟต์แวร์แบบกล่อง ไลบรารี และเฟิร์มแวร์ ที่รีลีสต้องผ่านการรีวิวของร้านแอปหรือถูกติดตั้งที่ลูกค้า

ข้อเสีย: บรานช์และการ merge มีจำนวนมาก บรานช์ฟีเจอร์ที่อยู่นานสะสมความต่างจาก develop และการ merge สองครั้งของ release และ hotfix ลืมได้ง่าย สำหรับเว็บเซอร์วิสที่ดีพลอยวันละหลายครั้ง บรานช์ release กลายเป็นภาระเอกสาร ผู้เขียนโมเดลเองเพิ่มหมายเหตุในปี 2020 ว่า สำหรับซอฟต์แวร์ที่ส่งมอบอย่างต่อเนื่องควรดูแนวทางที่เรียบง่ายกว่า เช่น GitHub Flow

GitHub Flow: บรานช์สั้นและ pull request

GitHub Flow คือ Feature Branch Workflow แบบเบา กฎสรุปได้ไม่กี่ข้อ:

  1. main พร้อมใช้งานเสมอและพร้อมดีพลอย
  2. งานทุกอย่างทำในบรานช์จาก main ที่มีชื่อสื่อความหมาย
  3. push บรานช์บ่อย ๆ และเปิด pull request (merge request) เพื่ออภิปราย
  4. หลังรีวิวและ CI ผ่าน จึง merge เข้า main
  5. สิ่งที่ merge เข้า main จะถูกดีพลอยทันที
git switch main
git pull
git switch -c feature/new-payment
# ... คอมมิตพร้อมข้อความที่เข้าใจได้ ...
git push -u origin feature/new-payment
# เปิด pull request ผ่านการรีวิวและ CI แล้ว merge เข้า main

ข้อดี: บรานช์ที่อยู่ยาวเพียงหนึ่งเดียว กฎน้อยที่สุด เส้นทางสั้นจากไอเดียถึงโปรดักชัน และการรีวิวโค้ดเป็นธรรมชาติ ข้อเสีย: ไม่มีที่สำหรับดูแลหลายเวอร์ชันหรือช่วงทำให้เสถียรก่อนรีลีส และหากไม่มีการทดสอบอัตโนมัติที่ดี "main พร้อมใช้งานเสมอ" จะเป็นเพียงความปรารถนา

แนวทางที่คล้ายกันแต่มีบรานช์สภาพแวดล้อมเพิ่ม (เช่น staging และ production) บางครั้งเรียกว่า GitLab Flow มีประโยชน์เมื่อการดีพลอยขึ้นโปรดักชันไม่ได้เกิดทันที แต่ตามกำหนดเวลาหรือหลังการตรวจสอบด้วยมือ

Trunk-based development

ใน trunk-based development ทุกคนรวมการเปลี่ยนแปลงเข้าบรานช์เดียว (trunk คือ main) บ่อย อย่างน้อยวันละครั้ง บรานช์ หากมี อยู่เพียงไม่กี่ชั่วโมง ไม่ใช่หลายสัปดาห์ ฟังก์ชันที่ยังไม่เสร็จเข้าสู่ main ได้ แต่ถูกซ่อนไว้หลัง feature flag:

if (featureFlags.isEnabled("new-checkout", user)) {
    renderNewCheckout();
} else {
    renderOldCheckout();
}

ข้อได้เปรียบหลักคือแทบไม่มีความขัดแย้งหนัก เพราะการเปลี่ยนแปลงมีขนาดเล็กและถูก merge ขณะที่ทุกคนยังจำบริบทได้ โมเดลนี้รองรับการอินทิเกรชันต่อเนื่องในความหมายตรงตัว และเหมาะกับทีมที่มีวุฒิภาวะ มี CI ที่เร็ว และมีการทดสอบครอบคลุมสูง บรานช์รีลีสยังได้รับอนุญาต แต่สร้างจาก trunk ทันทีก่อนปล่อย และการแก้ไขจะทำใน trunk ก่อน จากนั้นนำไปยังบรานช์รีลีสด้วย cherry-pick

ข้อเสีย: ต้องมีวินัยและโครงสร้างพื้นฐาน ได้แก่ การทดสอบที่รวดเร็ว flags และการมอนิเตอร์ สำหรับมือใหม่การคอมมิตลง main ทุกวันน่ากลัว และ CI ที่อ่อนแอทำให้ trunk กลายเป็นบรานช์ที่พังอยู่ตลอด flags ต้องลบเมื่อถึงเวลา ไม่เช่นนั้นโค้ดจะเต็มไปด้วยการแตกกิ่งที่ตายแล้ว

เปรียบเทียบ Git Flow, GitHub Flow และ trunk-based

เกณฑ์Git FlowGitHub FlowTrunk-based
บรานช์ที่อยู่ยาวmain และ developmainmain (trunk)
อายุของบรานช์ฟีเจอร์หลายวันถึงหลายสัปดาห์หลายวันไม่กี่ชั่วโมงหรือไม่มีบรานช์
ความถี่ของรีลีสเวอร์ชันตามแผนตามการ mergeต่อเนื่อง
การดูแลหลายเวอร์ชันได้ ผ่านแท็กและ hotfixไม่สะดวกผ่านบรานช์รีลีส
ข้อกำหนดของ CI และการทดสอบปานกลางสูงสูงมาก
ความเสี่ยงความขัดแย้งใหญ่สูงปานกลางต่ำ
เกณฑ์การเริ่มใช้ต้องเรียนรู้กฎน้อยที่สุดต้องมีวัฒนธรรมและ feature flags
โปรเจกต์ทั่วไปแอปมือถือ ไลบรารี ซอฟต์แวร์กล่องเว็บเซอร์วิส โอเพนซอร์ส สตาร์ทอัปผลิตภัณฑ์ใหญ่ที่ดีพลอยบ่อย

วิธีเลือก git workflow ให้ทีม

ตอบคำถามเหล่านี้:

  • ต้องดูแลรีลีสที่ปล่อยแล้วหลายเวอร์ชันหรือไม่? หากลูกค้าใช้ 2.x และ 3.x และทั้งสองต้องการแก้ไข ให้เลือก Git Flow หรือ trunk-based ที่มีบรานช์รีลีส
  • คุณดีพลอยบ่อยแค่ไหน? ปีละไตรมาส Git Flow ไม่เป็นอุปสรรค ทุกวัน เลือก GitHub Flow หรือ trunk-based
  • คุณเชื่อถือการทดสอบแค่ไหน? หาก CI ช้าและความครอบคลุมต่ำ เริ่มจาก GitHub Flow พร้อมการรีวิวบังคับ แล้วค่อย ๆ ทำให้บรานช์สั้นลง
  • ทีมมีขนาดและประสบการณ์เท่าไร? ทีมเล็กและมือใหม่ควรใช้ GitHub Flow มากที่สุด เพราะมีกฎข้อเดียว "main พร้อมทำงานเสมอ" และ pull request สำหรับทุกอย่าง

คำแนะนำเชิงปฏิบัติ: อย่าเลือก Git Flow เพียงเพราะ "ทำกันแบบนี้" เริ่มจากกระบวนการที่ง่ายที่สุดซึ่งแก้ปัญหาของคุณได้ และซับซ้อนขึ้นเมื่อมีปัญหาจริงปรากฏ เช่น ต้องการช่วงทำให้เสถียรหรือต้องดูแลเวอร์ชันเก่า

กฎที่ใช้ได้กับทุก workflow

  • ป้องกัน main บนเซิร์ฟเวอร์: ห้าม push ตรง ต้องมีการรีวิวและ CI ผ่านก่อน merge
  • ตกลงชื่อบรานช์: feature/, fix/, hotfix/ พร้อมหมายเลขงาน
  • ทำให้บรานช์สั้น ยิ่งบรานช์อยู่นาน การ merge ยิ่งเจ็บ ดึง main เข้าบรานช์ของตัวเองเป็นประจำด้วย merge หรือ rebase
  • เลือกกลยุทธ์การ merge ของ pull request (merge commit, squash หรือ rebase) และใช้อย่างสม่ำเสมอ
  • ทำเครื่องหมายรีลีสด้วยแท็กแบบ annotated ตาม semantic versioning

hotfix ในแต่ละโมเดล: บักเดียว สามกระบวนการ

ความต่างระหว่างแนวทางเห็นได้ชัดที่สุดในการแก้ไขด่วนบนโปรดักชัน

ใน Git Flow สร้าง hotfix/1.4.1 จาก main หลังแก้ไขแล้ว merge เข้า main พร้อมแท็ก v1.4.1 และต้อง merge เข้า develop ด้วย (หรือเข้ารีลีสปัจจุบันหากมี) การลืม merge ครั้งที่สองเป็นข้อผิดพลาดคลาสสิก บั๊กจะ "กลับมา" ในรีลีสถัดไป

git switch -c hotfix/1.4.1 main
git commit -am "แก้การคำนวณส่วนลด"
git switch main && git merge --no-ff hotfix/1.4.1
git tag -a v1.4.1 -m "Hotfix 1.4.1"
git switch develop && git merge --no-ff hotfix/1.4.1

ใน GitHub Flow hotfix คือบรานช์สั้นธรรมดาจาก main ที่มี pull request และได้รับลำดับความสำคัญในการรีวิว หลังการ merge มันขึ้นโปรดักชันด้วยการดีพลอยอัตโนมัติเดียวกัน ไม่มีกระบวนการแยก

ใน trunk-based การแก้ไขถูก commit ลง trunk ก่อน และถ้าโปรดักชันกำลังรันบรานช์รีลีส คอมมิตจะถูกนำไปที่นั่นด้วย git cherry-pick -x <แฮช> แฟล็ก -x เพิ่มการอ้างอิงคอมมิตต้นทางในข้อความ เพื่อให้ความเชื่อมโยงมองเห็นได้ในประวัติ แต่ถ้าปัญหาอยู่ที่ฟีเจอร์ใหม่ มักพอปิด feature flag ของมันโดยไม่ต้องแตะโค้ดเลย

ตอบคำถามในการสัมภาษณ์ว่า "คุณใช้ Git workflow แบบไหน"

ผู้สัมภาษณ์ไม่ได้ตรวจความรู้เรื่องชื่อ แต่ตรวจความเข้าใจการแลกเปลี่ยน คำตอบที่ดีคือบอกชื่อโมเดล อธิบายว่าบรานช์และการรีวิวทำงานอย่างไรในโมเดลนั้น และอธิบายว่าเหตุใดจึงเหมาะกับโปรเจกต์ ตัวอย่างเช่น "เราใช้ GitHub Flow: บรานช์จาก main, pull request พร้อมการรีวิวและ CI บังคับ หลัง merge จะดีพลอยอัตโนมัติ เราไม่ได้ใช้ Git Flow เพราะปล่อยรีลีสสัปดาห์ละหลายครั้ง และบรานช์รีลีสจะทำให้ช้าลงเปล่า ๆ" โบนัสคือการเล่าว่าคุณจะเปลี่ยนอะไรหากกระบวนการเติบโตขึ้น

สรุป

Git Flow เป็นโมเดลที่เข้มงวดพร้อมบรานช์ develop, release และ hotfix เหมาะกับผลิตภัณฑ์ที่มีเวอร์ชันตามแผนและต้องรองรับรีลีสหลายตัว GitHub Flow เรียบง่ายกว่าและเหมาะกับเว็บโปรเจกต์ส่วนใหญ่ ส่วน trunk-based development ให้ความเร็วสูงสุดแต่ต้องมี CI ที่สุกงอมและ feature flags เลือก git workflow ตามความถี่ของรีลีสและคุณภาพของการทดสอบ ไม่ใช่ตามกระแส วิธีทั้งสามพร้อมทีมงานและข้อผิดพลาดทั่วไปถูกอธิบายและฝึกจริงในหลักสูตร Git

คำถามที่พบบ่อย

Git Flow ล้าสมัยหรือไม่?

ไม่ แต่ขอบเขตการใช้งานแคบลง สำหรับเว็บเซอร์วิสที่ดีพลอยต่อเนื่องมันเกินความจำเป็น แต่สำหรับแอปมือถือ ไลบรารี และซอฟต์แวร์กล่องยังใช้ได้ดี

จำเป็นต้องมีบรานช์ develop หรือไม่หากรีลีสบ่อย?

โดยทั่วไปไม่จำเป็น develop ซ้ำกับ main และเพิ่มการ merge ที่ไม่จำเป็น เมื่อรีลีสบ่อยก็พอแล้วกับ main และบรานช์ฟีเจอร์สั้น ๆ

ใช้ rebase ใน Git Flow ได้หรือไม่?

ได้ คุณสามารถดึง develop เข้าบรานช์ฟีเจอร์ที่ยังไม่ได้ push หรือเป็นของตัวเองด้วย rebase แต่การ merge สุดท้ายเข้า develop และ main ในโมเดลคลาสสิกทำด้วย merge --no-ff เพื่อรักษาโครงสร้างของประวัติ

feature flag ดีกว่าบรานช์ฟีเจอร์ที่อยู่นานอย่างไร?

โค้ดรวมเข้า main ทันทีและไม่แตกต่างจากส่วนอื่นของโค้ดเบส การเปิดฟังก์ชันกลายเป็นการตัดสินใจแยกที่ย้อนกลับได้โดยไม่ต้องย้อนโค้ด

จะย้ายจาก Git Flow ไปเป็น GitHub Flow ได้อย่างไร?

ปิดรีลีสที่กำลังทำอยู่ merge develop เข้า main ปรับ CI ให้ดีพลอยจาก main แล้วลบ develop สร้างบรานช์ใหม่จาก main เท่านั้น

เรียนหัวข้อนี้ผ่านวิดีโอ

หัวข้อนี้สอนอย่างละเอียดพร้อมฝึกปฏิบัติในคอร์ส “Git с нуля до уверенного разработчика”

ไปที่คอร์ส