จัดการ git branch ยังไงให้มีประสิทธิภาพ
การใช้งาน git เราจะต้องมี branch มากกว่า 1 branch ถึงจะเรียกได้ว่าใช้งาน git ได้อย่างมีประสิทธิภาพจริงๆ เพราะ git นั้นไม่ใช่การ backup ถ้าคุณมี branch คุณจะสามารถสร้าง software หลาย version พร้อมๆกันได้ ไม่จะเป็นต้องรอให้ version 1.1 เสร็จก่อนถึงจะทำ 1.2 หรือต้องรอให้ 1.2 เสร็จก่อนถึงจะกลับไปแก้ bug ใน version 1.1 ได้ git branch จึงมาช่วยตอบโจทย์ต่างๆเหล่านี้
พยายามหลีกเลี่ยงการทำงานบน master branch หรือ main branch เพราะการย่้อนกลับไป version เดิมจะทำได้ยาก
ประโยชน์ที่ได้จากการแตก branch
- เราสามารถทำหลายๆ version พร้อมกันได้
- เราสามารถแตก branch ตาม environment ได้ เช่น branch dev, staging และ production
- เราสามารถพัฒนาหลายๆ edition พร้อมๆกันได้ เช่น version ที่ฟรี กับ version ที่ต้องมี subscription
- เราสามารถแตก branch ตาม maintainer(ผู้ดูแล source code ชุดนั้นๆ)
ไม่ว่าจะจัดการ branch แบบไหนสิ่งที่ต้องระวังคืออย่าให้มี long-lived branch หรือ long-living branch เกินกว่า 1 branch เพราะถือว่าเป็น anti-patterns เราควรแตก branch อกกมาชั่วคราวแล้วเมื่อเสร็จงานแล้วก็ merge กลับไปที่ branch หลัก(master branch หรือ main branch)
long-lived branch คือ branch ที่สร้างขึ้นมาแล้วอยู่ถาวร ไม่มีการ merge กลับเข้าสู่ main branch
คำสั่งที่ใช้ในการจัดการ branch
การจัดการ git branch เริ่มต้นจากคำสั่งแรกเราจะ list branch ที่มีบน local repository กับ branch บน remote repository ด้วยคำสั่ง
# แสดงรายชื่อ branch บน local repository
git branch
git branch --list
# แสดงรายชื่อเฉพาะ remote branch
git branch -r
# แสดงรายชื่อ branch ทั้งหมดทั้ง local และ remote branch
git branch -a
หลังจากนั้นให้ทำการสร้าง branch ขึ้นมาใหม่ด้วยคำสัง
# สร้าง branch ใหม่
git branch [ชื่อ branch ใหม่]
เมื่อสร้าง branch ใหม่แล้วจะมี pointer ที่เป็น branch และ HEAD อยู่ที่เดียวกัน
ถึงแม้ว่าเราจะสร้าง branch ขึ้นมาแล้ว แต่การ commit หลังจากนี้จะยังเกิดขึ้นบน branch เดิมจนกว่าเราจะย้ายไปยัง banch ใหม่ที่เราสร้างก่อน ซึ่งการย้ายไปยัง branch ใหม่เราจะใช้คำสั่ง
# ต้องเป็น branch ที่สร้างไว้อยู่แล้ว
git checkout [ชื่อ branch]
# หรือจะใช้ git switch แทน
git switch [ชื่อ branch]
หรือถ้าเราต้องการจะ checkout code ลงมาจาก remote repository และสร้าง branch ใหม่ไปพร้อมๆกันเราจะใช้คำสั่ง
git checkout -b [ชื่อ branch ใหม่ที่จะสร้าง]
# หรือจะใช้ git switch
git switch -c [ชื่อ branch ใหม่ที่จะสร้าง]
git switch จะเอาไว้ใช้แทนที่ git checkout เนื่องจากเข้าใจง่ายกว่าและใช้เฉพาะเรื่องมากกว่า
และหลังจากที่เราแก้ไข code ใน branch ใหม่เรียบร้อยแล้วเราจะทำการ merge กลับเข้า branch หลักด้วยคำสั่ง
# เราจะ merge กลับเข้าไปใน branch ไหนเราต้องอยู่ที่ branch นั้นก่อน
git checkout master # หรืออาจเป็น main branch ก็ได้
# git จะ merge จาก branch ที่เราใส่ตรงนี้กลับเข้ามายัง branch ที่เราอยู่ในปัจจุบัน
git merge [ชื่อ branch ที่แตกออกไป]
ในการทำงานจริงเราจะไม่ได้ merge เข้า master branch ตรงๆจาก command แบบนี้เราจะใช้การสร้าง pull request เพื่อตรวจสอบคุณภาพของ code ก่อนกลับเข้าสู่ master branch
หลังจากที่เราทำการ merge กลับเข้าไปแล้วเราควรจะลบ branch ที่ไม่ได้ใช้แล้วออกไปด้วย เพราะตามหลักการเราจะต้องมี long-lived branch ให้น้อยที่สุด ดังนั้นหลังจาก merge แล้วเราจะลบ branch นั้นออกด้วยคำสั่ง
git branch -d [ชื่อ branch ที่ต้องการลบ]
strategy ในการแตก branch
คำถามต่อไปที่เราจะเจอก็คือ เราจะแตก branch ยังไงดี? ซึ่งปัญหานี้ก็ได้มีเหล่ากูรูได้ให้คำแนะนำไว้ว่าการแตก branch ที่ดีนั้นควรจะเป็นไปตาม workflow ต่างๆเหล่านี้ แต่แน่นอนการใช้งานก็จะขึ้นอยู่กับ workflow ในการทำงานของเราเป็นหลัก git workflow เหล่านั้นเป็นเพียงข้อเสนอแนะเท่านั้น ลองมาดูการทำงานของ git workflow แต่ละตัวกัน
Gitflow
Gitflow เกิดขึ้นมาจากบทความ A successful git branching model ซึ่งถูกเขียนขึ้นมาตั้งแต่ปี 2010 โดย Vincent Driessen ซึ่งใน gitflow จะมีการแบ่ง branch ออกเป็น 2 กลุ่มคือ
-
Main branches เป็น long-lived branch(อยู่ถาวร) โดยที่ main branch นั้นจะมีอยู่ 2 branches คือ
- dev branch จะเป็น branch หลักในการทำงาน เวลาจะดู history จะดูที่ branch นี้
- master branch เราจะ merge กลับมาที่ master branch เมื่อมีการ release เพราะฉะนั้น HEAD ของ branch นี้ต้องพร้อมสำหรับการ deploy ไปยัง production เมื่อเราต้องการย้อนกลับไปยัง version ก่อนหน้าเราจะมาดูที่ branch นี้
-
Supporting branches เป็น short-lived branch(อยู่ชั่วคราว) ซึ่งจะประกอบไปด้วย
-
feature branches เป็นกลุ่มของ branch ที่แตกออกมาเพื่อสร้าง feature ใหม่หลังจากนั้นเมื่อทำเสร็จแล้วจะทำการ merge กลับไปที่ dev branch
-
release branches เราจะแตก branch ออกมาตาม release เป็นการเตรียมก่อนที่จะ merge กลับเข้าไปที่ master เมื่อเราแตก release branch ออกมาเราจะไม่เพิ่ม feature ใดๆเข้าไปใน release branch อีกแล้ว เราจะแก้ bug หรือ defect ให้เรียบร้อยแล้วเท่านั้น หลังจากนั้นเมื่อพร้อมแล้วเราก็จะ merge กลับไปที่ master branch
-
hotfix branches เราจะแตก branch ออกมาเพื่อทำการแก้ไข bug หรือ defect ต่างๆ
Supporting branch จะมีได้มากกว่า 1 branch เช่น feature branch จะแตกออกมาตาม feature ที่กำลังพัฒนา ซึ่งอาจมีมากกว่า 1 feature ก็ได้
ใน gitflow จะมี main branches อยู่ 2 branches คือ dev branch และ master branch ซึ่งเป็น long-lived branch(อยู่ตลอดไป) ซึ่งจะผิดไปจากหลักการ เพราะเมื่อเรามี continuous integration เราควรจะต้องมี long-lived branch เพียง branch เดียวเท่านั้น
การตั้งชื่อ branch สำหรับ gitflow
-
master branch เราจะตั้งชื่อ branch ตามนี้่
- main
- master
-
dev branch เราจะตั้งชื่อ branch ตามนี้่
- dev
- develop
-
feature branch เราจะตั้งชื่อโดยอธิบาย feature
- feature-description
- feature/description
-
release branch เราจะตั้งชื่อตาม release(อ้างอิงตาม semver)
- release-1.0.1
- release-1.2.0-rc1
- release/1.0.5
- release/1.3.0-beta
-
hotfix branch เราจะตั้งชื่อโดยอธิบาย bug หรือ defect ที่เกิดขึ้น
- hotfix-logo-alignment-issue
- bugfix-add-customer-issue
- hotfix/logo-alignment-issue
- bugfix/add-customer-issue
เนื่องจาก Gitflow นั้นถูกเขียนขึ้นมาตั้งแต่ปี 2010 ดังนั้นจึงไม่ค่อยเหมาะสมกับการทำงานในยุคปัจจุบันมากนัก
Githubflow
Githubflow น่าจะเป็น git workflow ที่นิยมใช้มากที่สุดเพราะเป็น workflow ที่เหมาะกับการทำงานที่มีหลายๆทีมมาทำงานร่วมกัน
ขั้นตอนของ githubflow นั้นจะประกอบไปด้วย
- Create branch สร้าง branch ออกมาซึ่งอาจเป็น feature branches หรือ hotfix ก็ได้
ในกรณีที่ไม่ได้อยู่ในทีมเดียวกันเราจะใช้การ fork repository ออกมาแทน
- Make changes หลังจากสร้าง branch ขึ้นมาแล้วเราก็ทำการเพิ่ม feature หรือแก้ไข defect ให้เรียบร้อย
- Create pull request เราจะไม่ให้ developer merge code กลับเข้าไปใน master branch ตรงๆ ต้องบังคับให้ส่ง pull request มาเท่านั้น developer ต้องไม่มีสิทธิ merge code เข้าไปใน master branch ได้
- Reviewing changes ส่วนนี้จะสร้างความแตกต่างให้กับ githubflow เพราะในเครื่องมือสมัยใหม่ทั้ง github, gitlab หรือ azure devops ล้วนแล้วแต่จะช่วยให้เราสื่อสารกับเพื่อนร่วมทีมได้ง่ายขึ้นทั้งการ comment และการ mention เพื่อนร่วมทีม(เหมือนใน social media ต่างๆ) ควรจะมีการ review code หรือตรวจสอบคุณภาพของ source code ในขั้นตอนนี้
ควรจะมี maintainer ที่คอยดูแล code ชุดนี้โดยเฉพาะ
- Merge pull request เมื่อทุกอย่างพร้อมแล้วเราก็จะทำการ merge กลับไปที่ master branch ซึ่งเราควรจะมี build pipline และ release pipeline สำหรับการ merge pull request โดยเฉพาะ
githubflow นั้นค่อนข้างเรียบง่ายไม่ซับซ้อน แถมยังช่วยกระตุ้นให้เกิดการสื่อสารกันภายในทีมมีมากขึ้นด้วย
Gitlabflow
gitlab workflow นั้นอาจไม่ได้รับความนิยมเท่ากับ githubflow แต่เป็นอีกหนึ่งแนวคิดที่น่าสนใจ เพราะมีการแตก branch ออกไปตาม environment ต่างๆ ซึ่งจุดนี้ทั้่ง githubflow และ gitflow ก็ไม่ได้พูดถึง
จากรูปจะเห็นว่า gitlab flow ทำการแตก branch ออกมาตาม environment ต่างๆ จาก staging -> pre-prod -> production ซึ่งถ้าเราอยากรู้ว่า code ชุดไหนที่อยู่บน production ล่าสุด ก็สามารถไปดูที่ production branch ได้เลย ซึ่งจริงๆแล้วเราอาจไม่ต้องมีครบทุก environment แบบนี้ก็ได้
สิ่งที่ทำให้ gitlabflow ต่างๆจาก githubflow คือ gitlabflow จะสามารถประยุกต์ได้กับ application ที่ไม่ได้มี continuous deployment คือไม่ได้ deploy ตลอดเวลา เช่น mobile application หรือ windows application ได้ด้วย
จากในรูปเราอาจทำการแยก release branch ออกมา ตาม version ต่างๆ เช่น 2.3-stable และ 2.4-stable ดังรูป
แต่ถ้าทำการแตก branch ออกไปในลักษณะนี้คำสั่งที่คุณต้องใช้คือ cherry-pick ซึ่ง cherry pick คือการ copy การเปลี่ยนแปลงที่เกิดขึ้นใน commit นั้น ไปใส่ไว้ใน branch ปัจจุบัน ซึ่งคำสั่งที่ใช้ในการทำ cherry pick คือ
# copy commit ที่ระบุใน parameter มาใส่ใน branch ปัจจุบบัน
git cherry-pick [commit hash]
# copy ตั้งแต่ commit-A นี้ถึง commit-B นี้
git cherry-pick [commit-A..commit-B]
# ใช้ --edit หรือ -e ในการเปลี่ยน commit message
git cherry-pick -e "commit message ที่ต้องการเปลี่ยน" [commit]
สิ่งที่ต้องระวังไว้เมื่อต้องใช้งาน cherry pick คือ
- cherry pick สามารถทำให้เกิด conflict ได้
- ไม่สามารถใช้ cherry pick กับ merge commit ได้
ในการแตก release branch ออกไปนั้นเมื่อเกิด bug หรือ defect ขึ้นสิ่งที่คุณต้องทำคือ แก้ไข bug หรือ defect นั้นบน main branch ก่อน(ตามหลักการ upstream first) แล้วหลังจากนั้นก็ทำการ cherry pick commit ที่เราแก้ bug หรือ defect นี้เข้าไปยัง release branch
จากที่อธิบายมาทั้งหมดจะเห็นว่าจริงๆแล้ว gitlabflow นั้นพยายามเพิ่มเติมสิ่งที่ยังไม่ได้เขียนไว้ใน githubflow ดังนั้นเวลาใช้งานเราจึงต้องยึดหลัการของ githubflow มาเป็นตัวตั้งต้นไว้ก่อน
แต่สุดท้ายแล้วไม่ว่าจะเป็น workflow ใดก็ตามเราควรคำนึงถึงความง่ายในการทำงานและความง่ายในการ tracking เป็นหลัก