Coding Gun

จัดการ 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

  1. เราสามารถทำหลายๆ version พร้อมกันได้
  2. เราสามารถแตก branch ตาม environment ได้ เช่น branch dev, staging และ production
  3. เราสามารถพัฒนาหลายๆ edition พร้อมๆกันได้ เช่น version ที่ฟรี กับ version ที่ต้องมี subscription
  4. เราสามารถแตก 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

create new git branch
ในตัวอย่างนี้เริ่มต้นมีแค่ master branch เท่านั้น

หลังจากนั้นให้ทำการสร้าง branch ขึ้นมาใหม่ด้วยคำสัง

# สร้าง branch ใหม่
git branch [ชื่อ branch ใหม่]

create new git branch
หลังจากสร้าง dev branch ขึ้นมาใหม่

เมื่อสร้าง branch ใหม่แล้วจะมี pointer ที่เป็น branch และ HEAD อยู่ที่เดียวกัน

ถึงแม้ว่าเราจะสร้าง branch ขึ้นมาแล้ว แต่การ commit หลังจากนี้จะยังเกิดขึ้นบน branch เดิมจนกว่าเราจะย้ายไปยัง banch ใหม่ที่เราสร้างก่อน ซึ่งการย้ายไปยัง branch ใหม่เราจะใช้คำสั่ง

# ต้องเป็น branch ที่สร้างไว้อยู่แล้ว
git checkout [ชื่อ branch]

# หรือจะใช้ git switch แทน
git switch [ชื่อ branch]

create new git branch
ย้าย HEAD ไปที่ dev branch ด้วย git checkout หรือ git switch

หรือถ้าเราต้องการจะ checkout code ลงมาจาก remote repository และสร้าง branch ใหม่ไปพร้อมๆกันเราจะใช้คำสั่ง

git checkout -b [ชื่อ branch ใหม่ที่จะสร้าง]

# หรือจะใช้ git switch
git switch -c [ชื่อ branch ใหม่ที่จะสร้าง]

git switch จะเอาไว้ใช้แทนที่ git checkout เนื่องจากเข้าใจง่ายกว่าและใช้เฉพาะเรื่องมากกว่า

และหลังจากที่เราแก้ไข code ใน branch ใหม่เรียบร้อยแล้วเราจะทำการ merge กลับเข้า branch หลักด้วยคำสั่ง

create new git branch
จะ merge กลับเข้า master branch ต้อง switch ไปที่ master branch ก่อน

# เราจะ merge กลับเข้าไปใน branch ไหนเราต้องอยู่ที่ branch นั้นก่อน
git checkout master # หรืออาจเป็น main branch ก็ได้

# git จะ merge จาก branch ที่เราใส่ตรงนี้กลับเข้ามายัง branch ที่เราอยู่ในปัจจุบัน
git merge [ชื่อ branch ที่แตกออกไป]

create new git branch
หลังจากสั่ง git merge จะเกิด commit ใหม่ขึ้นมาเพื่อรวม commit จากทั้ง 2 branches

ในการทำงานจริงเราจะไม่ได้ merge เข้า master branch ตรงๆจาก command แบบนี้เราจะใช้การสร้าง pull request เพื่อตรวจสอบคุณภาพของ code ก่อนกลับเข้าสู่ master branch

หลังจากที่เราทำการ merge กลับเข้าไปแล้วเราควรจะลบ branch ที่ไม่ได้ใช้แล้วออกไปด้วย เพราะตามหลักการเราจะต้องมี long-lived branch ให้น้อยที่สุด ดังนั้นหลังจาก merge แล้วเราจะลบ branch นั้นออกด้วยคำสั่ง

git branch -d [ชื่อ branch ที่ต้องการลบ]

create new git branch
หลังจากลบ dev branch ไปแล้วจะยังเหลือแต่ commit ที่อยู่ใน history เท่านั้น

strategy ในการแตก branch

คำถามต่อไปที่เราจะเจอก็คือ เราจะแตก branch ยังไงดี? ซึ่งปัญหานี้ก็ได้มีเหล่ากูรูได้ให้คำแนะนำไว้ว่าการแตก branch ที่ดีนั้นควรจะเป็นไปตาม workflow ต่างๆเหล่านี้ แต่แน่นอนการใช้งานก็จะขึ้นอยู่กับ workflow ในการทำงานของเราเป็นหลัก git workflow เหล่านั้นเป็นเพียงข้อเสนอแนะเท่านั้น ลองมาดูการทำงานของ git workflow แต่ละตัวกัน

Gitflow

Gitflow เกิดขึ้นมาจากบทความ A successful git branching model ซึ่งถูกเขียนขึ้นมาตั้งแต่ปี 2010 โดย Vincent Driessen ซึ่งใน gitflow จะมีการแบ่ง branch ออกเป็น 2 กลุ่มคือ

  1. 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 นี้
  2. Supporting branches เป็น short-lived branch(อยู่ชั่วคราว) ซึ่งจะประกอบไปด้วย

Supporting branch จะมีได้มากกว่า 1 branch เช่น feature branch จะแตกออกมาตาม feature ที่กำลังพัฒนา ซึ่งอาจมีมากกว่า 1 feature ก็ได้

Gitflow
ที่มา: https://nvie.com/posts/a-successful-git-branching-model

ใน gitflow จะมี main branches อยู่ 2 branches คือ dev branch และ master branch ซึ่งเป็น long-lived branch(อยู่ตลอดไป) ซึ่งจะผิดไปจากหลักการ เพราะเมื่อเรามี continuous integration เราควรจะต้องมี long-lived branch เพียง branch เดียวเท่านั้น

การตั้งชื่อ branch สำหรับ gitflow

  1. master branch เราจะตั้งชื่อ branch ตามนี้่

    • main
    • master
  2. dev branch เราจะตั้งชื่อ branch ตามนี้่

    • dev
    • develop
  3. feature branch เราจะตั้งชื่อโดยอธิบาย feature

    • feature-description
    • feature/description
  4. release branch เราจะตั้งชื่อตาม release(อ้างอิงตาม semver)

    • release-1.0.1
    • release-1.2.0-rc1
    • release/1.0.5
    • release/1.3.0-beta
  5. 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

ขั้นตอนของ githubflow นั้นจะประกอบไปด้วย

  1. Create branch สร้าง branch ออกมาซึ่งอาจเป็น feature branches หรือ hotfix ก็ได้

    ในกรณีที่ไม่ได้อยู่ในทีมเดียวกันเราจะใช้การ fork repository ออกมาแทน

  2. Make changes หลังจากสร้าง branch ขึ้นมาแล้วเราก็ทำการเพิ่ม feature หรือแก้ไข defect ให้เรียบร้อย
  3. Create pull request เราจะไม่ให้ developer merge code กลับเข้าไปใน master branch ตรงๆ ต้องบังคับให้ส่ง pull request มาเท่านั้น developer ต้องไม่มีสิทธิ merge code เข้าไปใน master branch ได้
  4. Reviewing changes ส่วนนี้จะสร้างความแตกต่างให้กับ githubflow เพราะในเครื่องมือสมัยใหม่ทั้ง github, gitlab หรือ azure devops ล้วนแล้วแต่จะช่วยให้เราสื่อสารกับเพื่อนร่วมทีมได้ง่ายขึ้นทั้งการ comment และการ mention เพื่อนร่วมทีม(เหมือนใน social media ต่างๆ) ควรจะมีการ review code หรือตรวจสอบคุณภาพของ source code ในขั้นตอนนี้

    ควรจะมี maintainer ที่คอยดูแล code ชุดนี้โดยเฉพาะ

  5. Merge pull request เมื่อทุกอย่างพร้อมแล้วเราก็จะทำการ merge กลับไปที่ master branch ซึ่งเราควรจะมี build pipline และ release pipeline สำหรับการ merge pull request โดยเฉพาะ

githubflow นั้นค่อนข้างเรียบง่ายไม่ซับซ้อน แถมยังช่วยกระตุ้นให้เกิดการสื่อสารกันภายในทีมมีมากขึ้นด้วย

Gitlabflow

gitlab workflow นั้นอาจไม่ได้รับความนิยมเท่ากับ githubflow แต่เป็นอีกหนึ่งแนวคิดที่น่าสนใจ เพราะมีการแตก branch ออกไปตาม environment ต่างๆ ซึ่งจุดนี้ทั้่ง githubflow และ gitflow ก็ไม่ได้พูดถึง

Gitlabflow
ที่มา: https://docs.gitlab.com/ee/topics/gitlab_flow.html

จากรูปจะเห็นว่า gitlab flow ทำการแตก branch ออกมาตาม environment ต่างๆ จาก staging -> pre-prod -> production ซึ่งถ้าเราอยากรู้ว่า code ชุดไหนที่อยู่บน production ล่าสุด ก็สามารถไปดูที่ production branch ได้เลย ซึ่งจริงๆแล้วเราอาจไม่ต้องมีครบทุก environment แบบนี้ก็ได้

สิ่งที่ทำให้ gitlabflow ต่างๆจาก githubflow คือ gitlabflow จะสามารถประยุกต์ได้กับ application ที่ไม่ได้มี continuous deployment คือไม่ได้ deploy ตลอดเวลา เช่น mobile application หรือ windows application ได้ด้วย

Gitlabflow
ที่มา: https://docs.gitlab.com/ee/topics/gitlab_flow.html

จากในรูปเราอาจทำการแยก 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 คือ

  1. cherry pick สามารถทำให้เกิด conflict ได้
  2. ไม่สามารถใช้ 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 เป็นหลัก

อ่านต่อเพิ่มเติมได้ที่นี่

Phanupong Permpimol
Follow me