Version Control and Git From Zero to Hero : Part 7.1 (Branch Management)
บทความโดย ผศ.ดร.ณัฐโชติ พรหมฤทธิ์
ภาควิชาคอมพิวเตอร์
คณะวิทยาศาสตร์
มหาวิทยาลัยศิลปากร
ถึงตอนนี้ผู้อ่านก็เข้าใจแนวคิดของ Branch กันแล้ว ซึ่งจะเห็นว่าเมื่อ 15 ปีที่แล้ว Linus ได้ออกแบบ Git Branch ไว้อย่างชาญฉลาดมาก เพราะ Branch ไม่ได้เป็นอะไรมากไปกว่า Pointer ของ Commit บน Timeline การทำงานกับ Branch จึงมีค่าใช้จ่ายที่ถูกอย่างไม่น่าเชื่อ ใน Part นี้เราจะฝึกปฏิบัติเพื่อจัดการกับ Branch ให้มีความชำนาญตามหัวข้อดังต่อไปนี้
- การสร้าง Branch
- การ Checkout Branch
- การ Push Branch ไปยัง Remote Project
- การ Fetch/Pull Branch
- การลบ Branch
- การ Merge Branch
- การแก้ปัญหาเมื่อเกิด Conflict
- การเคลื่อนย้าย Commit ระหว่าง Branch
- การกู้คืน Commit และไฟล์ที่ถูกสลายไปแล้ว
- การ Undo Commit อย่างปลอดภัย
- การสร้าง Branch ใหม่จากจุดต่างๆ บน Timeline
- การพัก Source Code ที่ยังไม่ได้ Check-In ไว้ชั่วคราว
- การตกแต่ง Branch
- การติด Tag
Initializing Git Branch
ให้สร้าง Project ใหม่ชื่อว่า gitbranch บน https://gitlab.cpsudevops.com หรือ https://gitlab.com
กลับมาที่ Local Host สร้าง Folder gitbranch โดยใช้คำสั่ง git init
git init
เชื่อมโยง Local Project กับ Remote Project โดยใช้คำสั่ง git remote
git remote add origin http://gitlab.cpsudevops.com/nuttachot/gitbranch.git
ปรับแต่ง Jupyter Notebook ให้สามารถบันทึก Source Code แบบ Plain Text (ดูรายละเอียดได้จาก Part 4) และ Config Git ไม่ให้ไป Track ไฟล์ *.ipynb โดยเพิ่มข้อความ *.ipynb ในไฟล์ .gitignore แล้ว Check-In ด้วยคำสั่ง git add และ git commit
git add .
git commit -m 'add gitignore'
ใช้คำสั่ง git log --stat เพื่อดู Commit และการเปลี่ยนแปลงของไฟล์
จากภาพเราจะเห็น HEAD Pointer ชี้ไปที่ Master Branch ซึ่งมีการสร้างมันขึ้นมาขณะที่ใช้คำสั่ง git init
git log --stat --oneline
เราสามารถดู Branch บน Local Host โดยใช้คำสั่ง git branch
git branch
เมื่อถึงขั้นตอนนี้ก็ให้ Sync History ของ Master Branch กับ Remote Project โดยใช้คำสั่ง git push
git push -u origin master
ตามมาตรฐานการพัฒนา Software สมัยใหม่ จะมีการสร้าง Branch ลบ Branch และ Mearge Branch กันเป็นประจำทุกๆ วัน ซึ่งคำสั่งสร้าง Branch ก็คือ git branch ตามด้วยชื่อของ Branch
ดังคำสั่งด้านล่างเราจะสร้าง Branch ใหม่ชื่อ Dev บน Local Host
git branch dev
จะเห็นว่าขณะนี้เรายังคง Check-Out ที่ Master Branch ตาม HEAD Pointer
เพื่อ Switch ไปยัง Dev Branch เราต้องใช้คำสั่ง git checkout เพื่อเคลื่อนย้าย HEAD Pointer
git checkout dev
เพื่อแสดงชื่อ Branch ทั้งบน Local Project และ Remote Project เราจะพิมพ์คำสั่ง git branch -a
git branch -a
Software Developers สามารถปรับปรุง Remote Branch Pointer ให้ Up-to-date ได้ตลอดเวลาโดยใช้คำสั่ง git fetch
git fetch
อย่างไรก็ตาม Git จะไม่สร้าง Test Branch บน Local Host จนกว่าจะใช้คำสั่ง git checkout --track เพื่อเชื่อมโยงกับ Test Branch บน Remote Project
git checkout --track origin/test
การลบ Branch บน Local Host ที่ไม่ได้ใช้งานแล้ว เช่น Test Branch จะต้อง Check-Out ไปที่ Branch อื่น แล้วใช้สำสั่ง git branch -d
git checkout dev
git branch -d test
ขณะที่การลบ Test Branch บน Remote Project จะใช้คำสั่งด้านล่าง
git push --delete origin test
หลังจากลบ Branch แล้ว ถ้าพบไฟล์ .swp ใน gitbranch Folder ให้ลบมัน ดังตัวอย่าง
rm .test.swp
Fast-Forward Merge
ลำดับต่อไปจะทดลองเขียนฟังก์ชัน add และ subtract บน Jupyter Notebook และ Check-In Source Code ทั้งหมด 2 ครั้ง เพื่อดูการ Merge Branch
git add .
git commit -m 'add add file'
git add .
git commit -m 'add subtract file'
History ของ Dev Branch จะมีหน้าตาดังภาพด้านล่าง
เพื่อที่จะ Sync History กับ Remote Project ในครั้งแรกของการ Sync เราต้องสร้างการเชื่อมโยง ด้วยคำสั่ง git push --set-upstream
git push --set-upstream origin dev
History ของ Dev Branch ที่ Local Project ถูก Sync กับ Remote Project ดังภาพ
ถึงตรงนี้เราจะ Merge Dev Branch เข้ากับ Master Branch ครับ
ก่อนอื่น Check-Out ที่ Master Branch แล้วใช้คำสั่ง git merge
git checkout master
git merge dev
จากภาพจะเห็นการ Merge แบบ Fast-forward ซึ่งมีการเคลื่อนย้าย Master Branch Pointer ไปข้างหน้ายัง Commit บนสุดของ Timeline ที่ตำแหน่งเดียวกับ Dev Branch Pointer
Three Way Merge and Conflict
เรากลับไปยัง Dev Branch ด้วยคำสั่ง git checkout ซึ่งทำให้มีการเคลื่อนย้าย HEAD Pointer ไปที่ Dev Branch ดังภาพ
git checkout dev
สร้าง Branch ใหม่ชื่อ Testing แล้ว Check-Out ที่ Testing Branch ในคราวเดียวกัน ด้วยคำสั่ง git checkout -b
git checkout -b testing
จะได้ Testing Branch Pointer ที่ชี้ไปยัง Commit เดียวกับ Dev Branch Pointer ดังภาพ
เราจะแตก History ออกเป็น 2 Timeline ด้วยการแก้ไข Code และ Check-In ไฟล์ add.Rmd ที่ Testing Branch และ Master Branch เพื่อทำ Three Way Merge
ที่ Test Branch แก้ไข Code ในไฟล์ add .Rmd ดังภาพ จากนั้นกด Save แล้ว Check-In
git add .
git commit -m 'edit add from testing branch'
Checkout ที่ Master Branch และ Refresh Browser แล้วแก้ไข Code ในไฟล์ add .Rmd ดังภาพ จากนั้นกด Save แล้ว Check-In
git checkout master
git add .
git commit -m 'edit add from master branch'
ตอนนี้เราจะมี Source Code อยู่ 2 Timeline แต่เมื่อใช้คำสั่ง git log เราจะเห็น History เฉพาะใน Master Branch เท่านั้น
git log --stat --oneline
เราสามารถดู Source Code ทั้ง 2 Timeline พร้อมกัน ด้วยคำสั่ง git log --all --graph ครับ
git log --stat --oneline --all --graph
เมื่อ Software Developers แก้ไข Source Code เสร็จแล้วจึง Merge Branch ที่ Stable น้อยกว่ากลับมายัง Branch หลักเช่น Master Branch
ใช้คำสั่ง git branch เพื่อให้แน่ใจว่าเราอยู่ใน Branch หลัก
git branch
เมื่อ Merge Testing Branch จาก Timeline หนึ่งกลับมายัง Master Branch ในอีก Timeline หนึ่ง ด้วยคำสั่ง git merge แล้ว Git จะสร้าง Version ของ Code ที่เป็นผลลัพธ์จากการเปรียบเทียบแบบ 3 ทาง คือระหว่าง Commit ที่เป็น Root ของทั้ง 2 Timeline ได้แก่ Commit ID 5a61112 (Base), Commit สุดท้ายของ Testing Brancg ได้แก่ Commit ID da7cf69 (Source) และ Commit สุดท้ายของ Master Branch ได้แก่ Commit ID 6429e89 (Target)
git merge testing
จากภาพด้านบนแสดง Source Code จากทั้ง Base, Source และ Target ที่นำมา Merge ได้เป็น Version ใหม่ดังภาพด้านล่าง
Three Way Merge ช่วยให้เกิดการ Merge แบบอัตโนมัติเมื่อเกิด Conflict ดังต่อไปนี้
%autosave 0 ในบรรทัดที่ 1 ของ Cell แรกไม่เกิด Conflict เพราะมี Code เหมือนกันทั้ง Source, Target และ Base
def add ในบรรทัดที่ 1 ของ Cell ที่ 2 เกิด Conflict เพราะมี Code ต่างกันทั้ง Source, Target และ Base ดังนั้น Git จึงแจ้งให้เราแก้ไข Conflict ด้วยมือ
Code บรรทัดที่ 2 ของ Cell ที่ 2 เกิด Conflict เพราะ Git มองว่ามีการแทรกคำสั่ง print(a + b ) ระหว่างคำสั่ง def add และ return ที่ Source ดังนั้น Git จึงเพิ่มบรรทัดใหม่แทรกระหว่าง def add และ return ที่ Target ให้ชั่วคราว ทำให้ทั้ง Source, Target และ Base มี Code ต่างกัน ดังนั้น Git จึงแจ้งให้เราแก้ Conflict ด้วยมือ
Return a + b ในบรรทัดสุดท้ายของ Cell ที่ 2 ไม่เกิด Conflict เพราะ Git มองว่าเป็น Code ที่เหมือนกันทั้ง Source, Target และ Base
add(1, 2) ในบรรทัดที่ 1 ของ Cell ที่ 3 เกิด Conflict เพราะ Source และ Base มี Code เหมือนกัน แต่ Target มี Code ต่างกัน ดังนั้น Git จึงเลือก Code ที่ Target เพราะมองว่าเป็น Code ที่ใหม่กว่า
เราเลือก Code จาก Source เพื่อแก้ไข Conflict ด้วยมือดังภาพ จากนั้นกด Save แล้ว Check-In
git add .
git commit -m 'resolved merge conflict'
Testing Branch จะถูก Merge เข้ากับ Master Branch ดังภาพด้านล่างครับ
ใน Part 7.1 นี้ เราได้ฝึกปฏิบัติเพื่อจัดการกับ Branch มาจนถึงหัวข้อ การแก้ปัญหาเมื่อเกิด Conflict ดังนั้นเพื่อไม่ให้เนื้อหายาวเกินไป ตั้งแต่หัวข้อ การเคลื่อนย้าย Commit ระหว่าง Branch เราจะฝึกปฏิบัติกันต่อใน Part 7.2 ครับ
- Version Control and Git From Zero to Hero : Part 1
- Version Control and Git From Zero to Hero : Part 2
- Version Control and Git From Zero to Hero : Part 3
- Version Control and Git From Zero to Hero : Part 4 (การใช้งาน Git ร่วมกับ Jupyter Notebook)
- Version Control and Git From Zero to Hero : Part 5 (Fixing the Mistakes)
- Version Control and Git From Zero to Hero : Part 6 (แนวคิดของ Branch)
- Version Control and Git From Zero to Hero : Part 7.1 (Branch Management)
รายวิชา Dev-Ops and Cloud Engineering 101