
ในโลกของการพัฒนาซอฟต์แวร์ที่ก้าวหน้าอย่างรวดเร็วในปัจจุบัน Git ได้กลายเป็นเครื่องมือที่ขาดไม่ได้สำหรับการควบคุมเวอร์ชันและการทำงานร่วมกัน แต่บ่อยครั้งที่เราพบว่าการใช้ Git ในระดับพื้นฐานนั้นอาจไม่เพียงพอที่จะตอบโจทย์ความซับซ้อนของการพัฒนาโปรเจกต์ขนาดใหญ่ที่ต้องทำร่วมกันเป็นทีม การจัดการ Branch ที่ยุ่งเหยิง, การแก้ Conflict ที่ซับซ้อน, หรือ History ที่อ่านยาก ล้วนเป็นปัญหาที่ทำให้ประสิทธิภาพของทีมลดลงได้ครับ
บทความนี้จะพาคุณเจาะลึกไปใน “Git Advanced Techniques สำหรับ Team Development” ซึ่งไม่ได้เป็นเพียงแค่การเรียนรู้คำสั่งใหม่ๆ เท่านั้น แต่เป็นการทำความเข้าใจปรัชญาเบื้องหลังเครื่องมือเหล่านี้ เพื่อให้ทีมของคุณสามารถทำงานร่วมกันได้อย่างราบรื่น มีประสิทธิภาพ และรักษาคุณภาพของโค้ดเบสได้อย่างยั่งยืน ตั้งแต่การเลือก Branching Strategy ที่เหมาะสม การรักษา Commit History ให้สะอาด ไปจนถึงการใช้เครื่องมือขั้นสูงเพื่อแก้ไขปัญหาต่างๆ เราจะครอบคลุมทุกแง่มุมที่จำเป็นในการยกระดับการทำงาน Git ของทีมคุณให้เหนือกว่าเดิมครับ
พร้อมแล้ว เรามาเริ่มต้นการเดินทางสู่การเป็น Git Master ในระดับทีมกันเลยดีกว่าครับ!
สารบัญ
- I. การจัดการ Branching Strategy ที่มีประสิทธิภาพ
- II. การรักษา History ที่สะอาดและอ่านง่าย
- III. การจัดการ Conflict อย่างมืออาชีพ
- IV. เครื่องมือช่วยการทำงานและ Workflow เพิ่มเติม
- V. การทำงานร่วมกับ Remote Repository และ Pull/Merge Requests
- คำถามที่พบบ่อย (FAQ)
- สรุปและ Call-to-Action
I. การจัดการ Branching Strategy ที่มีประสิทธิภาพ
Branching Strategy หรือกลยุทธ์การแตกสาขา เป็นหัวใจสำคัญของการทำงานร่วมกันด้วย Git ครับ การเลือกกลยุทธ์ที่เหมาะสมจะช่วยให้ทีมสามารถพัฒนาฟีเจอร์ใหม่ๆ แก้ไขบั๊ก และปล่อยเวอร์ชันได้อย่างเป็นระเบียบ ลดปัญหาการรวมโค้ด (Merge Conflict) และเพิ่มความรวดเร็วในการทำงาน บทความนี้จะเจาะลึก 3 กลยุทธ์หลักที่นิยมใช้กันแพร่หลายครับ
1.1 Git Flow: กลยุทธ์ที่แข็งแกร่งสำหรับโปรเจกต์ขนาดใหญ่
Git Flow เป็น Branching Model ที่ถูกนำเสนอโดย Vincent Driessen เป็นหนึ่งในกลยุทธ์ที่ได้รับความนิยมอย่างมากในโปรเจกต์ที่มีวงจรการพัฒนาที่ซับซ้อนและมีการปล่อยเวอร์ชันที่ชัดเจน (เช่น โปรเจกต์ซอฟต์แวร์ที่มี Major/Minor Release) ครับ
แนวคิดหลัก:
- Git Flow กำหนดให้มี Branch หลัก 2 สาขาที่ไม่มีวันถูกลบคือ
master(หรือmain) และdevelop - นอกจากนี้ยังมี Branch สำหรับสนับสนุนการทำงานเฉพาะกิจอีกหลายประเภท
สาขาหลัก (Main Branches):
-
master/main:- เป็น Branch ที่เก็บโค้ดสำหรับ Production เท่านั้น
- ทุก Commit ใน Branch นี้ควรเป็นโค้ดที่พร้อมใช้งานจริง (Release-ready)
- การรวมโค้ดเข้าสู่
masterมักจะมาจากการรวม Branchreleaseหรือhotfixเท่านั้น - เมื่อมีการรวมโค้ดเข้า
masterจะมีการ Tag เพื่อระบุเวอร์ชัน (เช่น v1.0, v1.1)
-
develop:- เป็น Branch หลักสำหรับการพัฒนาฟีเจอร์ใหม่ๆ
- เป็นที่รวมโค้ดจาก Branch
featureทั้งหมด - โค้ดใน
developควรจะเป็นโค้ดที่ค่อนข้างเสถียรและสามารถนำไปทดสอบได้
สาขาสนับสนุน (Supporting Branches):
-
featurebranches:- แตกออกมาจาก
developเพื่อพัฒนาฟีเจอร์ใหม่ๆ - แต่ละฟีเจอร์ควรมี 1
featurebranch เป็นของตัวเอง - เมื่อพัฒนาเสร็จสิ้น จะรวมกลับเข้าสู่
develop - ชื่อ Branch มักจะขึ้นต้นด้วย
feature/เช่นfeature/user-login
git checkout develop git checkout -b feature/new-dashboard # ทำงานพัฒนาฟีเจอร์... git add . git commit -m "Implement new dashboard UI" git checkout develop git merge --no-ff feature/new-dashboard git branch -d feature/new-dashboard - แตกออกมาจาก
-
releasebranches:- แตกออกมาจาก
developเมื่อพร้อมจะปล่อยเวอร์ชันใหม่ - ใช้สำหรับแก้ไขบั๊กเล็กๆ น้อยๆ, เตรียมไฟล์สำหรับการปล่อยเวอร์ชัน (เช่น update version number), และทำการทดสอบขั้นสุดท้าย
- เมื่อ
releasebranch พร้อม จะถูกรวมเข้าทั้งmaster(พร้อม Tag) และdevelop(เพื่อให้developมีการอัปเดตล่าสุด) - ชื่อ Branch มักจะขึ้นต้นด้วย
release/เช่นrelease/1.0.0
git checkout develop git checkout -b release/1.0.0 # แก้ไขบั๊ก, อัปเดตไฟล์เวอร์ชัน... git add . git commit -m "Prepare for release 1.0.0" git checkout master git merge --no-ff release/1.0.0 git tag -a 1.0.0 -m "Release 1.0.0" git checkout develop git merge --no-ff release/1.0.0 git branch -d release/1.0.0 - แตกออกมาจาก
-
hotfixbranches:- แตกออกมาจาก
masterเมื่อพบข้อผิดพลาดร้ายแรงใน Production ที่ต้องรีบแก้ไข - ใช้สำหรับแก้ไขบั๊กเร่งด่วนโดยเฉพาะ
- เมื่อแก้ไขเสร็จ จะถูกรวมเข้าทั้ง
master(พร้อม Tag เวอร์ชันใหม่) และdevelop(เพื่อให้developมีการแก้ไขบั๊กนั้นด้วย) - ชื่อ Branch มักจะขึ้นต้นด้วย
hotfix/เช่นhotfix/critical-bug-login
git checkout master git checkout -b hotfix/fix-login-bug # แก้ไขบั๊กเร่งด่วน... git add . git commit -m "Fix critical login bug" git checkout master git merge --no-ff hotfix/fix-login-bug git tag -a 1.0.1 -m "Hotfix for login bug" git checkout develop git merge --no-ff hotfix/fix-login-bug git branch -d hotfix/fix-login-bug - แตกออกมาจาก
ข้อดีของ Git Flow:
- โครงสร้างชัดเจน: มีโครงสร้าง Branch ที่ตายตัว ทำให้ทีมเข้าใจ Workflow ได้ง่าย
- แยกความรับผิดชอบ: แยก Branch สำหรับการพัฒนา, การปล่อยเวอร์ชัน, และการแก้ไขบั๊กออกจากกันอย่างชัดเจน
- เหมาะกับโปรเจกต์ขนาดใหญ่: ดีเยี่ยมสำหรับโปรเจกต์ที่มีการปล่อยเวอร์ชันแบบกำหนดเวลา และต้องการความมั่นคงสูง
ข้อเสียของ Git Flow:
- ซับซ้อน: มี Branch หลายประเภทและกฎการทำงานที่ค่อนข้างเข้มงวด อาจทำให้เกิดความสับสนสำหรับทีมที่ไม่คุ้นเคย
- Overhead สูง: การจัดการ Branch จำนวนมากอาจใช้เวลาและทรัพยากร
- ไม่เหมาะกับ CI/CD ที่รวดเร็ว: การมี Branch
developและmasterแยกกัน อาจทำให้การนำ Continuous Delivery ไปใช้ได้ยาก หากต้องการ deploy บ่อยครั้ง
อ่านเพิ่มเติมเกี่ยวกับการประยุกต์ใช้ Git Flow ในองค์กร
1.2 GitHub Flow และ GitLab Flow: ความเรียบง่ายสู่ Continuous Delivery
หาก Git Flow ดูจะซับซ้อนเกินไปสำหรับทีมที่เน้นความเร็วและการปล่อยเวอร์ชันบ่อยครั้ง GitHub Flow และ GitLab Flow อาจเป็นทางเลือกที่เหมาะสมกว่าครับ กลยุทธ์เหล่านี้เน้นความเรียบง่ายและสนับสนุนแนวคิด Continuous Integration/Continuous Delivery (CI/CD) ได้เป็นอย่างดี
GitHub Flow:
- แนวคิดหลัก: มี Branch หลักเพียง Branch เดียวคือ
master(หรือmain) - ทุกการพัฒนาฟีเจอร์หรือแก้ไขบั๊ก จะต้องทำบน
featurebranch ที่แยกออกมาจากmaster - เมื่อทำงานเสร็จ จะเปิด Pull Request (PR) หรือ Merge Request (MR) เพื่อให้เพื่อนร่วมทีมรีวิว
- เมื่อได้รับการอนุมัติและผ่านการทดสอบ (CI/CD) ก็สามารถรวม (Merge) เข้าสู่
masterได้ทันที - กฎสำคัญ:
masterBranch ต้องพร้อมสำหรับการ Deploy เสมอ (Always deployable)
git checkout main
git pull origin main # ดึงโค้ดล่าสุด
git checkout -b feature/add-user-profile
# ทำงานพัฒนาฟีเจอร์...
git add .
git commit -m "Add user profile page"
git push origin feature/add-user-profile
# เปิด Pull Request/Merge Request บนแพลตฟอร์ม
# เมื่อได้รับการอนุมัติและ Merge เข้า main แล้ว
git checkout main
git pull origin main
git branch -d feature/add-user-profile
GitLab Flow:
- แนวคิดหลัก: คล้ายกับ GitHub Flow แต่มีความยืดหยุ่นในการเพิ่ม Branch อื่นๆ เข้ามาได้ตามความจำเป็น
- GitLab Flow แนะนำให้มี Branch หลัก
mainที่ deploy ได้เสมอ - อาจมี Branch
productionแยกต่างหากหากต้องการความมั่นคงที่สูงขึ้นสำหรับการ deploy ที่ไม่บ่อยครั้ง - อาจมี Branch
pre-productionหรือstagingสำหรับการทดสอบก่อนขึ้น Production - ยังคงเน้นการใช้ Feature Branch และ Merge Request เป็นหลัก
ข้อดีของ GitHub/GitLab Flow:
- เรียบง่าย: มีกฎและ Branch น้อยกว่า ทำให้ง่ายต่อการเรียนรู้และนำไปใช้
- สนับสนุน CI/CD: การรวมโค้ดเข้า
mainบ่อยครั้งช่วยให้มั่นใจได้ว่าโค้ดพร้อมสำหรับการ Deploy อยู่เสมอ - ลด Merge Conflict: การรวมโค้ดบ่อยครั้งและ Branch ที่มีอายุสั้น ช่วยลดโอกาสเกิด Conflict ขนาดใหญ่
ข้อเสียของ GitHub/GitLab Flow:
- ความมั่นคง: หากไม่มีกระบวนการทดสอบที่เข้มแข็ง การรวมโค้ดบ่อยครั้งอาจทำให้เกิดปัญหาใน Production ได้ง่าย
- การจัดการ Release: อาจไม่เหมาะกับโปรเจกต์ที่ต้องการควบคุม Release Version อย่างเข้มงวดแบบมี Major/Minor Version ที่ชัดเจน
1.3 Trunk-Based Development: มุ่งเน้นการรวมโค้ดบ่อยครั้ง
Trunk-Based Development (TBD) เป็นกลยุทธ์ที่เน้นการทำงานบน Branch หลัก (trunk หรือ main) โดยตรง หรือใช้ Feature Branch ที่มีอายุสั้นมากๆ (short-lived branches) และรวมโค้ดกลับเข้า Branch หลักให้บ่อยที่สุดเท่าที่จะทำได้ อาจจะเป็นวันละหลายครั้ง หรืออย่างน้อยวันละครั้งครับ
แนวคิดหลัก:
- ลดความซับซ้อนของ Branching ให้เหลือน้อยที่สุด
- เน้นการ Integrate โค้ดอย่างต่อเนื่อง
- ใช้ Feature Flag (หรือ Toggle) ในการเปิด/ปิดฟีเจอร์ที่ยังไม่สมบูรณ์ แทนการเก็บฟีเจอร์เหล่านั้นไว้ใน Feature Branch นานๆ
git checkout main
git pull origin main # ดึงโค้ดล่าสุด
# อาจจะทำงานบน main โดยตรง หรือสร้าง short-lived branch
git checkout -b feature/small-task # ทำงานเล็กๆ ที่เสร็จเร็ว
# ทำงานพัฒนาฟีเจอร์...
git add .
git commit -m "Implement small change A"
git push origin feature/small-task
# เปิด PR/MR และรีบรวมเข้า main ภายในไม่กี่ชั่วโมง
ข้อดีของ Trunk-Based Development:
- Continuous Integration: เป็นหัวใจของ CI ทำให้สามารถตรวจจับปัญหาได้ตั้งแต่เนิ่นๆ
- ลด Merge Conflict: การรวมโค้ดบ่อยครั้งทำให้ Conflict มีขนาดเล็กและแก้ไขได้ง่าย
- ความเร็ว: Workflow ที่เรียบง่ายช่วยให้ทีมสามารถพัฒนาและ Deploy ได้อย่างรวดเร็ว
- ลด “Merge Hell”: หลีกเลี่ยงปัญหาการรวมโค้ดที่ค้างไว้นานๆ
ข้อเสียของ Trunk-Based Development:
- ต้องการวินัยสูง: ทีมต้องมีวินัยในการ Commit บ่อยๆ และต้องแน่ใจว่าโค้ดที่ Commit นั้นไม่ทำให้ Branch หลักเสีย
- การทดสอบอัตโนมัติ: จำเป็นต้องมีชุดการทดสอบอัตโนมัติ (Unit Test, Integration Test) ที่แข็งแกร่งและระบบ CI/CD ที่มีประสิทธิภาพสูง
- Feature Flag: การจัดการ Feature Flag อาจเพิ่มความซับซ้อนในการจัดการโค้ดในบางสถานการณ์
1.4 ตารางเปรียบเทียบ Branching Strategies
เพื่อช่วยให้คุณตัดสินใจได้ง่ายขึ้นว่า Branching Strategy แบบใดที่เหมาะสมกับทีมและโปรเจกต์ของคุณ ลองดูตารางเปรียบเทียบนี้ครับ
| คุณสมบัติ | Git Flow | GitHub Flow / GitLab Flow | Trunk-Based Development |
|---|---|---|---|
| จำนวน Branch หลัก | 2 (master, develop) |
1 (main/master) (+ อาจมี production/staging สำหรับ GitLab Flow) |
1 (trunk/main) |
| ความซับซ้อน | สูง | ปานกลางถึงต่ำ | ต่ำ |
| วงจรการ Release | กำหนดเวลา, Major/Minor Release ชัดเจน | Continuous Delivery, Release บ่อยครั้ง | Continuous Delivery, Release บ่อยมาก |
| อายุของ Feature Branch | ปานกลางถึงยาว | สั้น | สั้นมาก (ไม่กี่ชั่วโมงถึง 1 วัน) |
| การจัดการ Conflict | อาจมี Conflict ขนาดใหญ่หาก Feature Branch ค้างนาน | Conflict ขนาดเล็ก จัดการง่ายกว่า | Conflict น้อยมาก เล็กและง่ายต่อการแก้ไข |
| เหมาะสำหรับ | โปรเจกต์ที่มี Lifecycle ที่ชัดเจน, ซอฟต์แวร์ที่ต้องมีการ Release เป็นเวอร์ชัน | โปรเจกต์ที่ต้องการความเร็ว, Web Application, SaaS, CI/CD | โปรเจกต์ที่ต้องการความเร็วสูงสุด, High-performing teams, CI/CD เข้มข้น |
| ข้อกำหนดเพิ่มเติม | ทีมต้องเข้าใจ Workflow อย่างดี | main ต้อง Deployable เสมอ, CI/CD |
CI/CD, Automated Tests, Feature Flags, วินัยของทีมสูง |
การเลือก Branching Strategy ที่ดีที่สุดนั้นขึ้นอยู่กับขนาดของทีม, ลักษณะของโปรเจกต์, ความถี่ในการ Release, และระดับความต้องการในการควบคุมเวอร์ชันของคุณครับ สิ่งสำคัญคือการสื่อสารและตกลงกันในทีมให้ชัดเจนว่าจะใช้กลยุทธ์ใด เพื่อให้ทุกคนทำงานไปในทิศทางเดียวกันครับ
II. การรักษา History ที่สะอาดและอ่านง่าย
Commit History ที่สะอาดและเป็นระเบียบเปรียบเสมือนสมุดบันทึกการเปลี่ยนแปลงโค้ดที่อ่านง่าย ช่วยให้ทีมสามารถทำความเข้าใจการพัฒนา ตรวจสอบย้อนหลัง หรือแก้ไขปัญหาได้ง่ายขึ้นครับ การมี History ที่ยุ่งเหยิงเต็มไปด้วย Commit ที่ไม่สื่อความหมาย หรือ Merge Commit ที่ไม่จำเป็น จะทำให้การทำงานยากขึ้นอย่างมาก เทคนิคเหล่านี้จะช่วยให้คุณรักษา History ที่น่าอ่านได้ครับ
2.1 Git Rebase: จัดระเบียบ History ให้เป็นเส้นตรง
git rebase เป็นหนึ่งในคำสั่ง Git ที่ทรงพลัง แต่ก็อันตรายหากใช้ผิดวิธีครับ มันถูกใช้เพื่อย้าย หรือ “re-apply” ชุดของ Commit ไปยัง Base Commit ใหม่ ทำให้ Commit History เป็นเส้นตรงและอ่านง่ายขึ้น
การทำงานของ git rebase:
สมมติว่าคุณกำลังทำงานบน feature/A ที่แตกมาจาก main และในขณะที่คุณทำงานอยู่นั้น main ก็มี Commit ใหม่ๆ เพิ่มเข้ามา หากคุณ git merge main เข้า feature/A จะทำให้เกิด Merge Commit ที่ไม่จำเป็น แต่ถ้าคุณ git rebase main จะเกิดอะไรขึ้น?
- Git จะหา Commit ที่
feature/Aและmainแตกต่างกัน - Git จะ “ยกเลิก” Commit ของ
feature/Aชั่วคราว - Git จะอัปเดต
feature/Aให้เป็นสถานะล่าสุดของmain - จากนั้น Git จะ “re-apply” Commit ของ
feature/Aที่ยกเลิกไว้ ไปบนmainล่าสุด
ผลลัพธ์คือ History ของ feature/A จะดูเหมือนว่ามันถูกสร้างขึ้นมาจาก main ล่าสุด โดยไม่มี Merge Commit ที่ไม่จำเป็น ทำให้ History เป็นเส้นตรงครับ
ข้อดีของ git rebase:
- History ที่เป็นเส้นตรง: ทำให้การไล่ดูประวัติการเปลี่ยนแปลงโค้ดง่ายขึ้น
- ลด Merge Commit: หลีกเลี่ยงการสร้าง Merge Commit ที่ไม่จำเป็น ทำให้ Graph ดูสะอาดตา
- แก้ไข Commit ก่อน Push:
git rebase -iช่วยให้คุณแก้ไข, รวม, หรือลบ Commit ก่อนที่จะ Push ขึ้น Remote ได้
ข้อควรระวัง: ห้าม rebase public branches!
กฎทองของการใช้ git rebase คือ ห้าม rebase Branch ที่ถูก Push ขึ้น Remote และมีคนอื่นใช้งานอยู่เด็ดขาด (Never rebase a public/shared branch)
เหตุผลคือ git rebase จะสร้าง Commit ใหม่ที่มี Hash แตกต่างจาก Commit เดิม หากมีคนอื่นดึง Branch นั้นไปทำงานแล้ว และคุณ rebase แล้ว Push –force ขึ้นไป จะทำให้ History ของ Branch บน Local ของคนอื่นกับ Remote ไม่ตรงกัน และจะทำให้เกิดความสับสนและปัญหาในการทำงานร่วมกันได้ครับ
ควรใช้ git rebase กับ Branch ที่คุณเป็นเจ้าของคนเดียว หรือ Feature Branch ที่ยังไม่ได้ถูก Push ขึ้น Remote หรือ Push ขึ้นไปแล้วแต่ยังไม่มีใครดึงไปใช้งานครับ
git rebase -i (Interactive Rebase): เครื่องมือทรงพลังในการจัดการ Commit
Interactive Rebase (`-i` ย่อมาจาก interactive) ช่วยให้คุณสามารถแก้ไข Commit History ได้อย่างละเอียด ก่อนที่จะรวมโค้ดเข้า Branch หลัก หรือก่อนที่จะ Push ขึ้น Remote ครับ
คำสั่งพื้นฐาน: git rebase -i HEAD~N (N คือจำนวน Commit ที่คุณต้องการย้อนกลับไปแก้ไข)
git checkout feature/your-branch
git rebase -i HEAD~3 # แก้ไข 3 Commit ล่าสุด
เมื่อรันคำสั่งนี้ Git จะเปิด Text Editor ขึ้นมาแสดงรายการ Commit พร้อมตัวเลือกต่างๆ:
pick 623b32a Fix typo in README
pick c78d21f Implement user registration form
pick a1b2c3d Add validation to user input
# Rebase 6f1a3b2..a1b2c3d onto 6f1a3b2 (3 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) for each commit
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, fixup --create-automsg <commit> = like "fixup", but create commit message
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
ตัวเลือกที่พบบ่อย:
pick: ใช้ Commit นั้นตามปกติ (ค่าเริ่มต้น)reword: ใช้ Commit นั้น แต่จะเปิด Text Editor ให้คุณแก้ไข Commit Messageedit: ใช้ Commit นั้น แต่จะหยุดการ Rebase ชั่วคราว ให้คุณแก้ไขโค้ด หรือเพิ่ม/ลบไฟล์ได้ ก่อนจะgit commit --amendแล้วgit rebase --continuesquash: รวม Commit ปัจจุบันเข้ากับ Commit ก่อนหน้า (หมายเลข Commit ที่อยู่ข้างบน) โดย Git จะเปิด Text Editor ให้คุณรวม Commit Message ของทั้งสอง Commitfixup: เหมือนsquashแต่จะทิ้ง Commit Message ของ Commit ปัจจุบันไป โดยใช้ Commit Message ของ Commit ก่อนหน้าแทน เหมาะสำหรับ Commit ที่เป็นการแก้ไขเล็กน้อยของ Commit ก่อนหน้าdrop: ลบ Commit นั้นทิ้งไปเลย
ตัวอย่างการใช้งาน git rebase -i เพื่อ clean up commits ก่อน PR:
สมมติว่าคุณมี Commit ดังนี้:
git log --oneline
a1b2c3d Add validation to user input
c78d21f Implement user registration form
623b32a Fix typo in README
คุณต้องการรวม “Add validation to user input” เข้ากับ “Implement user registration form” และเปลี่ยนข้อความของ “Fix typo in README” ให้ชัดเจนขึ้น:
git rebase -i HEAD~3
ใน Text Editor คุณจะเปลี่ยนจาก:
pick 623b32a Fix typo in README
pick c78d21f Implement user registration form
pick a1b2c3d Add validation to user input
เป็น:
reword 623b32a Fix typo in README
pick c78d21f Implement user registration form
squash a1b2c3d Add validation to user input
เมื่อบันทึกและปิด Text Editor, Git จะดำเนินการ Rebase:
1. ให้คุณแก้ไข Commit Message ของ 623b32a (เช่น เป็น “Docs: Correct typo in README”)
2. รวม a1b2c3d เข้ากับ c78d21f และให้คุณแก้ไข Commit Message สำหรับ Commit ที่รวมกันใหม่ (เช่น “Feat: Implement user registration with validation”)
ผลลัพธ์คือคุณจะมี Commit ที่สะอาดและสื่อความหมายมากขึ้นครับ
2.2 Git Merge Strategies: เลือกวิธีรวมโค้ดให้เหมาะสม
นอกจากการใช้ git rebase แล้ว การรวม Branch ด้วย git merge ก็มีกลยุทธ์ที่แตกต่างกันไป ซึ่งส่งผลต่อ Commit History ครับ
-
Fast-forward Merge (Default):
- เกิดขึ้นเมื่อ Branch ที่คุณกำลังจะรวมเข้า (เช่น
feature/A) เป็น “forward” ของ Branch เป้าหมาย (เช่นmain) หมายความว่าmainไม่มี Commit ใหม่ๆ เกิดขึ้นหลังจากที่feature/Aแตกตัวออกมา - Git จะไม่สร้าง Merge Commit ใหม่ แต่จะแค่เลื่อน HEAD ของ
mainไปยัง Commit ล่าสุดของfeature/A - ทำให้ History เป็นเส้นตรงและไม่มี Merge Commit
git checkout main git merge feature/A # หากเป็น fast-forward Git จะไม่สร้าง Merge Commit - เกิดขึ้นเมื่อ Branch ที่คุณกำลังจะรวมเข้า (เช่น
-
--no-ff(No Fast-forward Merge):- บังคับให้ Git สร้าง Merge Commit เสมอ แม้ว่าจะเป็น Fast-forward Merge ก็ตาม
- ข้อดีคือทำให้เห็นว่ามีการรวม Branch เกิดขึ้น ณ จุดใดใน History
- เหมาะสำหรับ Branching Strategy ที่ต้องการรักษา “เหตุการณ์” การรวม Branch ไว้ใน History (เช่น Git Flow)
git checkout develop git merge --no-ff feature/new-dashboard -
--squash:- รวม Commit ทั้งหมดจาก Branch ที่กำลังจะรวม เข้าเป็น Commit เดียวบน Branch เป้าหมาย
- Git จะเตรียม Staging Area ให้เหมือนกับการรวม Branch แต่จะไม่มีการ Commit อัตโนมัติ
- คุณต้อง
git commitเอง และเขียน Commit Message ที่รวมเนื้อหาของ Commit ทั้งหมดจาก Branch ที่ถูก Squash - Branch ที่ถูก Squash จะไม่ปรากฏใน History แต่เนื้อหาจะถูกรวมเข้ามาใน Commit เดียว
- เหมาะสำหรับรวม Feature Branch เข้าสู่ Branch หลัก เพื่อให้ History สะอาดตาและแต่ละฟีเจอร์มีเพียง Commit เดียวเท่านั้น
git checkout main git merge --squash feature/big-feature git commit -m "Feat: Implement big feature with all its sub-tasks" git branch -d feature/big-feature # ลบ feature branch หลัง squash merge
2.3 Git Commit Best Practices: สร้าง Commit ที่มีคุณภาพ
Commit Message ที่ดีเป็นสิ่งสำคัญไม่แพ้โค้ดที่ดีครับ มันช่วยให้ทีมเข้าใจว่าการเปลี่ยนแปลงนี้ทำอะไร ทำไมถึงทำ และเมื่อไหร่
-
Atomic Commits:
- แต่ละ Commit ควรมีการเปลี่ยนแปลงที่เล็กพอและเป็นอิสระต่อกัน (Atomic)
- แต่ละ Commit ควรทำสิ่งเดียวและทำสิ่งนั้นได้ดี
- ช่วยให้ง่ายต่อการ Revert, Bisect, หรือทำความเข้าใจการเปลี่ยนแปลง
-
Commit Message Convention (เช่น Conventional Commits):
- ใช้รูปแบบ Commit Message ที่สอดคล้องกันทั่วทั้งทีม
- ตัวอย่างรูปแบบ:
<type>(<scope>): <subject> <type>: เช่นfeat(feature),fix(bug fix),docs(documentation),style(formatting),refactor(code refactoring),test(adding tests),chore(maintenance)<scope>(optional): ระบุส่วนของโปรเจกต์ที่ได้รับผลกระทบ เช่น(auth),(ui)<subject>: สรุปการเปลี่ยนแปลงสั้นๆ ไม่เกิน 50-72 ตัวอักษร ขึ้นต้นด้วยคำกริยาในรูปปัจจุบัน (imperative mood) เช่น “Add”, “Fix”, “Update”- สามารถเพิ่ม Body (รายละเอียดเพิ่มเติม) และ Footer (เช่น Breaking Changes, Closes #123) ได้
feat(user): Add new user profile page This commit introduces a new user profile page accessible via /profile. It includes basic user information display and an option to edit the profile. Resolves: #123 See also: #456การใช้ Convention ช่วยให้สามารถสร้าง Changelog อัตโนมัติ หรือ Trigger CI/CD ได้
-
git commit --amend:- ใช้เพื่อแก้ไข Commit ล่าสุดที่คุณเพิ่งทำไป
- เหมาะสำหรับแก้ไขข้อผิดพลาดเล็กๆ น้อยๆ ใน Commit Message หรือเพิ่ม/ลบไฟล์ที่คุณลืมใส่ไปใน Commit ล่าสุด
- จะสร้าง Commit Hash ใหม่
- เช่น คุณลืม
git add .ไฟล์บางไฟล์ก่อน commit:git add forgotten_file.js git commit --amend --no-edit # เพิ่มไฟล์เข้าไปใน commit ล่าสุด โดยไม่เปลี่ยน commit messageหรือต้องการแก้ไข commit message:
git commit --amend # จะเปิด editor ให้แก้ไข commit message - เช่นเดียวกับ
git rebaseควรใช้กับ Commit ที่ยังไม่ได้ Push ขึ้น Remote ครับ
ศึกษาเพิ่มเติมเกี่ยวกับ Conventional Commits
III. การจัดการ Conflict อย่างมืออาชีพ
Merge Conflict เป็นสิ่งที่หลีกเลี่ยงไม่ได้ในการทำงานเป็นทีม แต่การรู้วิธีจัดการกับมันอย่างมีประสิทธิภาพจะช่วยลดความหงุดหงิดและทำให้ Workflow ของทีมราบรื่นขึ้นครับ
3.1 ทำความเข้าใจ Conflict ประเภทต่างๆ
Conflict เกิดขึ้นเมื่อ Git ไม่สามารถรวมการเปลี่ยนแปลงจากสอง Branch เข้าด้วยกันได้โดยอัตโนมัติ โดยส่วนใหญ่เกิดจาก:
- Content Conflict: สอง Branch มีการเปลี่ยนแปลงบรรทัดเดียวกันในไฟล์เดียวกัน
- Rename/Delete Conflict: ไฟล์ถูกเปลี่ยนชื่อหรือลบใน Branch หนึ่ง แต่ถูกแก้ไขในอีก Branch หนึ่ง
- Tree Conflict: โครงสร้างไดเรกทอรีชนกัน เช่น ไฟล์ถูกย้ายไปที่อื่นใน Branch หนึ่ง แต่ถูกสร้างใหม่ในชื่อเดียวกันในอีก Branch หนึ่ง
เมื่อเกิด Conflict, Git จะหยุดกระบวนการ Merge/Rebase และแจ้งให้คุณแก้ไขด้วยตนเองครับ
3.2 เครื่องมือช่วยแก้ Conflict
การแก้ไข Conflict ด้วย Text Editor อย่างเดียวอาจเป็นเรื่องที่ท้าทาย โดยเฉพาะอย่างยิ่งกับไฟล์ขนาดใหญ่หรือ Conflict ที่ซับซ้อน เครื่องมือช่วยแก้ Conflict (Merge Tools) จะช่วยให้กระบวนการนี้ง่ายขึ้นมากครับ
- IDE Built-in Tools: IDE ยอดนิยมส่วนใหญ่ (เช่น VS Code, IntelliJ IDEA, WebStorm) มี Merge Tool ในตัวที่ใช้งานง่าย แสดงโค้ดจากทั้งสอง Branch และช่วยให้คุณเลือกการเปลี่ยนแปลงที่ต้องการ หรือแก้ไขด้วยตนเอง
- External Merge Tools: เช่น Beyond Compare, KDiff3, Meld, P4Merge คุณสามารถตั้งค่า Git ให้ใช้ Merge Tool เหล่านี้ได้ โดยใช้คำสั่ง
git configgit config --global merge.tool meld # ตั้งค่าให้ใช้ Meld เป็น merge toolจากนั้นเมื่อเกิด Conflict คุณสามารถใช้คำสั่ง
git mergetoolเพื่อเปิด Merge Tool ที่ตั้งค่าไว้ได้เลยครับgit mergetool
3.3 ขั้นตอนการแก้ Conflict อย่างเป็นระบบ
เมื่อเกิด Conflict, Git จะแทรก “Conflict Markers” เข้าไปในไฟล์ที่มีปัญหา ซึ่งจะบอกว่าส่วนไหนมาจาก Branch ใด
function greet(name) {
<<<<<<< HEAD
console.log(`Hello, ${name}!`); // โค้ดจาก HEAD (Current Branch)
=======
console.log(`Hi there, ${name}.`); // โค้ดจาก Branch ที่กำลังจะรวมเข้ามา
>>>>>>> feature/greeting-update
}
<<<<<< HEAD: จุดเริ่มต้นของส่วนโค้ดจาก Branch ปัจจุบัน (ที่คุณอยู่)=======: ตัวคั่นระหว่างโค้ดจากสอง Branch>>>>>> feature/greeting-update: จุดสิ้นสุดของส่วนโค้ดจาก Branch ที่กำลังรวมเข้ามา
ขั้นตอนการแก้ Conflict:
-
ตรวจสอบสถานะ:
git statusGit จะบอกคุณว่าไฟล์ใดบ้างที่มี Conflict
- เปิดไฟล์ที่มี Conflict: ใช้ Text Editor หรือ IDE ของคุณเปิดไฟล์เหล่านั้น
-
แก้ไข Conflict: ตัดสินใจว่าจะเก็บโค้ดส่วนไหน, รวมโค้ดจากทั้งสองส่วน, หรือเขียนโค้ดใหม่ทั้งหมด ลบ Conflict Markers ออกทั้งหมดเมื่อแก้ไขเสร็จสิ้น
function greet(name) { console.log(`Hello there, ${name}! How are you?`); // แก้ไขให้เป็นโค้ดที่ถูกต้อง } -
เพิ่มไฟล์ที่แก้ไขแล้ว:
git add <file-with-conflict>ทำซ้ำสำหรับทุกไฟล์ที่มี Conflict
-
Commit การแก้ไข Conflict:
git commitGit จะสร้าง Commit Message อัตโนมัติสำหรับการแก้ไข Conflict คุณสามารถแก้ไขได้ตามต้องการ
3.4 ความแตกต่างในการแก้ Conflict ระหว่าง Rebase และ Merge
-
Merge Conflict:
- เมื่อเกิด Conflict ระหว่าง
git mergeคุณจะต้องแก้ Conflict ทั้งหมดในครั้งเดียว แล้วgit addและgit commitเพื่อสร้าง Merge Commit - การแก้ Conflict เกิดขึ้นเพียงครั้งเดียว (หรือหลายครั้งหากมีการรวมหลาย Branch พร้อมกัน)
- เมื่อเกิด Conflict ระหว่าง
-
Rebase Conflict:
- เมื่อเกิด Conflict ระหว่าง
git rebaseGit จะหยุดที่ Commit ที่เกิด Conflict - คุณต้องแก้ Conflict สำหรับ Commit นั้น,
git addไฟล์ที่แก้ไข, จากนั้นgit rebase --continue - กระบวนการนี้จะทำซ้ำสำหรับแต่ละ Commit ที่มี Conflict
- ข้อดีคือช่วยให้คุณแก้ไข Conflict ในบริบทของแต่ละ Commit ได้ดีขึ้น
- ข้อเสียคืออาจต้องแก้ Conflict หลายครั้งหากมีหลาย Commit ที่ทับซ้อนกัน
# เมื่อเกิด Conflict ระหว่าง rebase git status # ดูไฟล์ที่มี Conflict # แก้ไขไฟล์... git add <file-with-conflict> git rebase --continue # ไปยัง commit ถัดไปที่มี conflict (ถ้ามี) หรือจบกระบวนการ rebase - เมื่อเกิด Conflict ระหว่าง
การฝึกฝนเป็นกุญแจสำคัญในการจัดการ Conflict ครับ ยิ่งคุณเจอและแก้ไข Conflict บ่อยแค่ไหน คุณก็จะยิ่งชำนาญมากขึ้นเท่านั้นครับ
IV. เครื่องมือช่วยการทำงานและ Workflow เพิ่มเติม
นอกจากคำสั่งพื้นฐานแล้ว Git ยังมีเครื่องมือและฟีเจอร์ขั้นสูงอีกมากมายที่สามารถช่วยให้ทีมทำงานได้อย่างมีประสิทธิภาพมากขึ้น จัดการกับสถานการณ์ที่ไม่คาดฝัน และสร้าง Workflow ที่เป็นอัตโนมัติครับ
4.1 Git Stash: พักงานชั่วคราวอย่างมีประสิทธิภาพ
git stash เป็นคำสั่งที่ช่วยให้คุณสามารถเก็บงานที่คุณยังทำไม่เสร็จ (ไฟล์ที่ถูกแก้ไขหรือ Staged) ไว้ชั่วคราว เพื่อที่คุณจะสามารถสลับไปทำงานบน Branch อื่นได้โดยไม่ต้อง Commit งานที่ยังไม่สมบูรณ์ครับ
สถานการณ์ที่ใช้ git stash:
- คุณกำลังทำงานบน Feature Branch แต่มี Hotfix ด่วนเข้ามา
- คุณต้องการดึง Pull ล่าสุดจาก Remote แต่มีงานที่ยังไม่ Commit อยู่
- คุณต้องการทดลองโค้ดบางอย่างใน Branch อื่นอย่างรวดเร็ว
คำสั่งที่สำคัญ:
-
git stash save "message": เก็บงานปัจจุบันเข้า Stash พร้อมข้อความอธิบายgit stash save "Work in progress for user profile"หากไม่ใส่ข้อความ จะใช้ Commit Message ของ HEAD ล่าสุด (ถ้ามี)
หรือใช้แค่git stashเพื่อเก็บงานทั้งหมดที่ถูก tracked -
git stash list: แสดงรายการ Stash ทั้งหมดที่คุณเก็บไว้git stash list # stash@{0}: On feature/user-profile: Work in progress for user profile # stash@{1}: On main: Before trying new experimental feature -
git stash apply <stash_id>: นำงานจาก Stash ที่ระบุกลับมาใช้ โดย Stash นั้นจะยังคงอยู่ในรายการgit stash apply stash@{0} # นำ stash ล่าสุดกลับมาใช้หากไม่ระบุ
stash_idจะ applystash@{0}(stash ล่าสุด) -
git stash pop <stash_id>: นำงานจาก Stash ที่ระบุกลับมาใช้ และลบ Stash นั้นออกจากรายการgit stash pop # นำ stash ล่าสุดกลับมาใช้และลบออกจากรายการ -
git stash drop <stash_id>: ลบ Stash ที่ระบุออกจากรายการgit stash drop stash@{1} -
git stash clear: ลบ Stash ทั้งหมดออกจากรายการ -
git stash show <stash_id>: แสดงรายละเอียดการเปลี่ยนแปลงใน Stash
git stash เป็นเครื่องมือเล็กๆ ที่ช่วยชีวิตได้หลายครั้งในสถานการณ์ฉุกเฉินครับ
4.2 Git Reflog: ย้อนเวลาหา Commit ที่หายไป
คุณเคยลบ Branch ผิด หรือ Rebase พลาดจน Commit หายไปไหมครับ? git reflog คือฮีโร่ที่จะช่วยคุณกู้สถานการณ์เหล่านั้นได้ครับ
git reflog (Reference Log) จะบันทึกทุกการเคลื่อนไหวของ HEAD ของคุณ ไม่ว่าจะเป็นการ Commit, Merge, Rebase, Reset, Checkout, หรือ Stash. มันคือ “บันทึกกิจกรรม” ของคุณใน Git Repository บน Local ครับ
การใช้งาน:
git reflog
# a1b2c3d HEAD@{0}: commit: Add new feature
# c78d21f HEAD@{1}: rebase (finish): returning to refs/heads/feature/branch
# 623b32a HEAD@{2}: rebase (start): checkout feature/branch
# ...
แต่ละบรรทัดจะแสดงการดำเนินการ (เช่น commit, rebase, checkout) และ Commit Hash ที่ HEAD ชี้ไป ณ เวลานั้น
ตัวอย่างการกู้คืน Commit:
สมมติว่าคุณเผลอ git reset --hard HEAD~1 ไป แต่ต้องการกลับไปที่ Commit ที่ถูกลบไปแล้ว
- รัน
git reflog - หา Commit Hash ก่อนที่คุณจะรัน
git reset --hard(เช่นa1b2c3dในตัวอย่างด้านบน) - ใช้
git reset --hard a1b2c3d(หรือgit reset --hard HEAD@{n}โดย n คือตำแหน่งใน reflog) เพื่อย้าย HEAD กลับไปที่ Commit นั้น
git reflog เป็น Lifesaver ที่แท้จริงในการกู้คืนงานที่ดูเหมือนจะหายไปครับ
4.3 Git Hooks: อัตโนมัติงานด้วยอีเวนต์ Git
Git Hooks คือสคริปต์ที่ Git สามารถรันได้โดยอัตโนมัติเมื่อเกิดเหตุการณ์บางอย่างขึ้นใน Repository ของคุณ (เช่น ก่อน Commit, หลัง Commit, ก่อน Push) ช่วยให้คุณสามารถบังคับใช้กฎของทีม, ทำการตรวจสอบโค้ดอัตโนมัติ หรือ Trigger CI/CD ได้ครับ
สคริปต์เหล่านี้อยู่ในไดเรกทอรี .git/hooks/ ใน Repository ของคุณ และมักจะเป็น Shell Script หรือสคริปต์ภาษาอื่นๆ
ประเภทของ Git Hooks:
-
Client-side Hooks: รันบนเครื่องของนักพัฒนาเอง
-
pre-commit: รันก่อนที่จะ Commit- ใช้เพื่อตรวจสอบ Linting, Formatting, Run Unit Tests เล็กๆ, หรือตรวจสอบ Commit Message
- หากสคริปต์คืนค่าเป็น non-zero จะยกเลิกการ Commit
#!/bin/sh # .git/hooks/pre-commit # ตรวจสอบโค้ดด้วย ESLint npm run lint if [ $? -ne 0 ]; then echo "ESLint found issues. Please fix them before committing." exit 1 fi # ตรวจสอบว่ามี console.log() เหลืออยู่ไหม if git diff --cached | grep -q "console.log"; then echo "Warning: console.log() found in staged changes. Did you mean to commit this?" # exit 1 # ถ้าต้องการบังคับไม่ให้ commit fi exit 0 -
prepare-commit-msg: รันหลังจากpre-commitและก่อนเปิด Editor เพื่อเขียน Commit Message- ใช้เพื่อสร้าง Commit Message เริ่มต้น หรือบังคับใช้ Commit Message Template
-
commit-msg: รันหลังจากเขียน Commit Message เสร็จ- ใช้เพื่อตรวจสอบความถูกต้องของ Commit Message (เช่น ตรงตาม Conventional Commits หรือไม่)
-
post-commit: รันหลังจาก Commit สำเร็จ- ใช้เพื่อแจ้งเตือน, อัปเดตไฟล์บางอย่าง, หรือ Trigger การทำงานอื่นๆ
-
pre-push: รันก่อนที่จะ Push ขึ้น Remote- ใช้เพื่อรัน Integration Tests, ตรวจสอบว่า Branch ที่ Push ถูกต้องหรือไม่
-
post-checkout: รันหลังจาก Checkout Branch สำเร็จ- ใช้เพื่อติดตั้ง Dependencies ใหม่ (เช่น
npm install) หรือ Rebuild โปรเจกต์
- ใช้เพื่อติดตั้ง Dependencies ใหม่ (เช่น
-
-
Server-side Hooks: รันบน Server ที่เก็บ Remote Repository
-
pre-receive: รันเมื่อมี Push เข้ามา แต่ก่อนที่ Git จะยอมรับการเปลี่ยนแปลง- ใช้เพื่อบังคับใช้กฎที่เข้มงวด เช่น ห้าม Push เข้า
mainโดยตรง, ตรวจสอบการอนุญาต, ตรวจสอบ Commit Message
- ใช้เพื่อบังคับใช้กฎที่เข้มงวด เช่น ห้าม Push เข้า
-
post-receive: รันหลังจาก Git ยอมรับการเปลี่ยนแปลง- ใช้เพื่อ Trigger CI/CD Pipeline, อัปเดตเว็บไซต์, ส่งอีเมลแจ้งเตือน
-
การใช้งานในทีม:
Git Hooks ใน .git/hooks/ เป็น Local สำหรับแต่ละ Repository เท่านั้น หากต้องการใช้ Hooks ร่วมกันในทีม คุณจะต้องหาวิธีแชร์ Hooks เหล่านี้ (เช่น ใช้ Git Submodule, หรือ Copy สคริปต์เข้าไปใน Repository และให้แต่ละคนติดตั้งเอง) หรือใช้เครื่องมืออย่าง Husky (สำหรับ JavaScript/Node.js) ที่ช่วยจัดการ Client-side Hooks ได้ง่ายขึ้นครับ
4.4 Git Bisect: ค้นหา Bug อย่างรวดเร็ว
เมื่อมี Bug เกิดขึ้นในโค้ดเบสขนาดใหญ่ การหาว่า Commit ไหนเป็นต้นเหตุของ Bug อาจเป็นเรื่องที่ยากและใช้เวลานาน git bisect เป็นเครื่องมือที่ยอดเยี่ยมในการทำ “Binary Search” เพื่อค้นหา Commit ที่ทำให้เกิด Bug ได้อย่างรวดเร็วครับ
วิธีการใช้งาน:
-
เริ่มต้น Bisect:
git bisect start -
ระบุ Commit ที่มี Bug (Bad Commit): ซึ่งมักจะเป็น Commit ล่าสุดที่คุณรู้ว่ามี Bug
git bisect bad # หรือ git bisect bad <commit_hash_bad> -
ระบุ Commit ที่ไม่มี Bug (Good Commit): ซึ่งมักจะเป็น Commit เก่ากว่าที่คุณรู้ว่าโค้ดยังทำงานถูกต้อง
git bisect good <commit_hash_good>Git จะทำการ Checkout ไปที่ Commit ตรงกลางระหว่าง Commit Good และ Bad
-
ทดสอบโค้ด: ณ Commit ที่ Git Checkout ไป คุณต้องทำการทดสอบว่า Bug ยังคงอยู่หรือไม่
- ถ้ามี Bug:
git bisect bad - ถ้าไม่มี Bug:
git bisect good
Git จะทำการ Checkout ไปยัง Commit ถัดไป (ซึ่งเป็น Commit กลางระหว่างช่วงที่เหลือ) และทำซ้ำขั้นตอนที่ 4
- ถ้ามี Bug:
-
สิ้นสุด Bisect: เมื่อ Git ระบุ Commit ต้นเหตุของ Bug ได้แล้ว มันจะแจ้งให้คุณทราบ
git bisect resetเพื่อกลับไปยัง Branch เดิมที่คุณอยู่ก่อนเริ่มต้น Bisect
git bisect สามารถทำให้การหา Bug ที่ใช้เวลาหลายชั่วโมงเหลือเพียงไม่กี่นาทีครับ
4.5 Git Cherry-pick: เลือก Commit ที่ต้องการ
git cherry-pick เป็นคำสั่งที่ช่วยให้คุณสามารถเลือก Commit เดียว (หรือหลาย Commit) จาก Branch หนึ่ง แล้วนำมา “apply” บน Branch ปัจจุบันของคุณได้ครับ คล้ายกับการ “คัดลอก” Commit นั้นมา
สถานการณ์ที่ใช้ git cherry-pick:
- คุณต้องการนำ Hotfix ที่ทำบน Production Branch มาใช้ใน Development Branch
- คุณทำ Commit บางอย่างบน Branch ผิด และต้องการย้าย Commit นั้นมายัง Branch ที่ถูกต้อง
- คุณต้องการนำ Commit บางส่วนจาก Feature Branch ของเพื่อนร่วมทีมมาใช้ใน Branch ของคุณ
การใช้งาน:
git checkout target-branch # ไปยัง branch ที่ต้องการนำ commit มาใส่
git cherry-pick <commit_hash> # ระบุ hash ของ commit ที่ต้องการ
หากมี Conflict เกิดขึ้น คุณต้องแก้ไข Conflict, git add ., และ git cherry-pick --continue ครับ
ข้อควรระวัง:
- การ Cherry-pick จะสร้าง Commit ใหม่ที่มีเนื้อหาเหมือนเดิม แต่มี Commit Hash ใหม่
- หากคุณ Cherry-pick Commit ที่มีอยู่แล้ว แล้วรวม Branch ที่มี Commit เดิมเข้ามาในภายหลัง อาจทำให้เกิด Commit ซ้ำซ้อนและ History ที่สับสนได้
- ควรใช้
git cherry-pickด้วยความระมัดระวังและเมื่อจำเป็นเท่านั้นครับ หากเป็นไปได้ การ Merge หรือ Rebase อาจเป็นทางเลือกที่ดีกว่า
V. การทำงานร่วมกับ Remote Repository และ Pull/Merge Requests
การทำงานเป็นทีมย่อมหมายถึงการทำงานร่วมกับ Remote Repository ครับ การเข้าใจวิธีการซิงค์โค้ด, การตั้งค่า Remote และการใช้ Pull/Merge Requests อย่างมีประสิทธิภาพเป็นสิ่งสำคัญอย่างยิ่งในการรักษา Workflow ของทีมให้ราบรื่นครับ
5.1 การตั้งค่าและจัดการ Remote Repository
Remote Repository คือ Git Repository ที่อยู่บน Server เช่น GitHub, GitLab, Bitbucket ที่ทีมใช้เป็นศูนย์กลางในการแลกเปลี่ยนโค้ด
-
การเพิ่ม Remote:
git remote add origin https://github.com/user/repo.gitoriginคือชื่อย่อที่ใช้เรียก Remote Repository นี้ (เป็นชื่อที่นิยมใช้สำหรับ Remote หลัก) -
การดู Remote ที่มีอยู่:
git remote -v # origin https://github.com/user/repo.git (fetch) # origin https://github.com/user/repo.git (push)จะแสดง URL ของ Remote สำหรับการ Fetch และ Push
-
การเปลี่ยนชื่อ Remote:
git remote rename origin upstreamเปลี่ยนชื่อ Remote จาก
originเป็นupstream -
การลบ Remote:
git remote remove origin
บางทีมอาจมี Remote มากกว่าหนึ่ง เช่น origin สำหรับ Fork ของตัวเอง และ upstream สำหรับ Repository ต้นฉบับครับ
5.2 Fetch, Pull, Push: การซิงค์โค้ดอย่างเข้าใจ
สามคำสั่งนี้เป็นแกนหลักของการทำงานกับ Remote Repository แต่มีความแตกต่างที่สำคัญ
-
git fetch:- ดาวน์โหลดข้อมูลจาก Remote Repository มายัง Local Repository ของคุณ
- จะไม่รวมการเปลี่ยนแปลงเข้ากับ Branch ปัจจุบันของคุณ
- มันจะอัปเดต Remote Tracking Branches (เช่น
origin/main) เท่านั้น - ช่วยให้คุณสามารถดูการเปลี่ยนแปลงล่าสุดของ Remote ได้โดยไม่กระทบงานที่คุณทำอยู่
git fetch origin # ดึงข้อมูลจาก remote ชื่อ origin git log origin/main # ดู commit บน main ของ remote -
git pull:- คือการทำ
git fetchตามด้วยgit merge(ค่าเริ่มต้น) หรือgit rebase - จะดาวน์โหลดการเปลี่ยนแปลงจาก Remote และรวมเข้ากับ Branch ปัจจุบันของคุณทันที
- อาจทำให้เกิด Conflict หากมีการเปลี่ยนแปลงที่ทับซ้อนกัน
git pull origin main # ดึงจาก main ของ origin และ merge เข้า branch ปัจจุบันคุณสามารถตั้งค่าให้
git pullใช้ rebase แทน merge ได้ด้วย:git config --global pull.rebase trueหรือใช้คำสั่งเฉพาะกิจ
git pull --rebase origin main - คือการทำ
-
git push:- อัปโหลด Commit จาก Local Repository ของคุณไปยัง Remote Repository
- ต้องแน่ใจว่า Local Branch ของคุณอัปเดตและไม่มี Conflict กับ Remote ก่อน Push
- หากคุณกำลัง Push ขึ้น Branch ที่มีคนอื่นทำงานอยู่ อาจต้อง
git pullก่อน
git push origin feature/my-new-feature # Push branch ไปยัง remote git push -u origin feature/my-new-feature # ตั้งค่า upstream เพื่อให้ push/pull ครั้งต่อไปง่ายขึ้นgit push --forceหรือgit push -f:ใช้เพื่อบังคับ Push โดยไม่สนใจว่า Remote Branch มี Commit ที่ Local Branch ไม่มีหรือไม่ ควรใช้ด้วยความระมัดระวังอย่างยิ่ง และใช้เมื่อคุณเข้าใจผลกระทบแล้วเท่านั้น (เช่น หลังจาก
git rebaseFeature Branch ของตัวเองที่ยังไม่มีใครใช้)
5.3 Pull Requests / Merge Requests Best Practices
Pull Request (GitHub, Bitbucket) หรือ Merge Request (GitLab) เป็นกลไกสำคัญในการทำงานร่วมกัน ช่วยให้ทีมสามารถรีวิวโค้ด, พูดคุย, และทดสอบการเปลี่ยนแปลงก่อนที่จะรวมเข้าสู่ Branch หลัก
-
การเขียน Description ที่ดี:
- อธิบายว่า PR นี้ทำอะไร, ทำไมถึงทำ, และแก้ไขปัญหาอะไร
- รวมถึงวิธีการทดสอบ (ถ้าจำเป็น), รูปภาพ/วิดีโอ (ถ้าเป็น UI), หรือผลกระทบที่อาจเกิดขึ้น
- เชื่อมโยงกับ Issue Tracker (เช่น Jira, GitHub Issues)
-
การรีวิวโค้ด (Code Review):
- เป็นกระบวนการที่สำคัญที่สุดใน PR
- ตรวจสอบคุณภาพโค้ด, ความถูกต้อง, ประสิทธิภาพ, Security, และการปฏิบัติตาม Coding Standards
- ให้ Feedback ที่สร้างสรรค์และเฉพาะเจาะจง
- ใช้ Tools ช่วยในการรีวิว (เช่น Comment ใน GitHub/GitLab)
-
การใช้ CI/CD ใน PR:
- ตั้งค่าให้ระบบ CI/CD ทำงานอัตโนมัติเมื่อมี PR ใหม่
- รัน Unit Test, Integration Test, Linting, และ Build Project
- หาก CI/CD ไม่ผ่าน PR ไม่ควรถูก Merge
-
เลือกวิธีการ Merge ที่เหมาะสม:
- Merge Commit: (Default) สร้าง Merge Commit ใหม่เมื่อรวม Branch เหมาะสำหรับ Git Flow หรือเมื่อต้องการบันทึกเหตุการณ์การ Merge
- Squash and Merge: รวม Commit ทั้งหมดใน PR ให้เป็น Commit เดียว แล้ว Merge เข้า Branch หลัก ทำให้ History สะอาดตา เหมาะสำหรับ GitHub Flow หรือ Trunk-Based Development
- Rebase and Merge: ทำการ Rebase Feature Branch บน Branch หลักก่อน แล้ว Fast-forward Merge เข้าไป ทำให้ History เป็นเส้นตรง เหมาะสำหรับ Trunk-Based Development ที่ต้องการ History ที่เป็น Linear
การเลือกวิธี Merge ควรตกลงกันในทีมครับ
การใช้ Pull Request อย่างมีคุณภาพจะช่วยยกระดับมาตรฐานโค้ดของทีมและลดความเสี่ยงในการนำ Bug เข้าสู่ Production ได้อย่างมากครับ
เรียนรู้เพิ่มเติมเกี่ยวกับแนวทางปฏิบัติในการทำ Code Review
คำถามที่พบบ่อย (FAQ)
Q1: เมื่อไหร่ควรใช้ rebase เมื่อไหร่ควรใช้ merge?
A1: การเลือกระหว่าง rebase และ merge ขึ้นอยู่กับ Workflow ของทีมและผลลัพธ์ของ Commit History ที่ต้องการครับ
-
ใช้
git rebaseเมื่อ:- คุณต้องการให้ Commit History เป็นเส้นตรง (Linear History) และสะอาดตา ไม่มี Merge Commit ที่ไม่จำเป็น
- คุณกำลังทำงานบน Feature Branch ของตัวเองที่ยังไม่ได้ Push ขึ้น Remote หรือมีคนอื่นใช้งานอยู่
- คุณต้องการรวม Commit หลายๆ อันให้เป็น Commit เดียว (Squash) ก่อนที่จะเปิด Pull Request
- คุณต้องการ “