Merge Conflict เกิดขึ้นได้อย่างไร? เข้าใจสาเหตุเพื่อป้องกัน

Merge Conflict คืออะไร

Merge conflict เกิดเมื่อ Git ไม่สามารถอัตโนมัติรวม (merge) ไฟล์ได้ เพราะบรรทัดเดียวกันถูกแก้ไขจากสองที่ต่างกัน ปัญหานี้เป็นสิ่งที่หลีกเลี่ยงไม่ได้ในการทำงานแบบทีมหรือ collaborative development โดยเฉพาะเมื่อหลายคนกำลังแก้ไขไฟล์เดียวกัน Merge conflict ไม่ใช่ข้อผิดพลาด แต่เป็นสัญญาณให้ Git ขอความช่วยเหลือจากคุณในการตัดสินใจว่าควรเลือกเวอร์ชันใด ความเข้าใจเกี่ยวกับสาเหตุและวิธีป้องกัน merge conflict จะช่วยให้การพัฒนาบนเซิร์ฟเวอร์ Cloud VPS ของ ผู้ให้บริการโฮสติ้ง เป็นไปอย่างราบรื่น

1. บรรทัดเดียวกันถูกแก้ไขจาก 2 branch

นี่เป็นสาเหตุที่พบบ่อยที่สุด เมื่อสองคนแก้ไขบรรทัดเดียวกันในไฟล์เดียวกัน โดยแก้ไขให้แตกต่างกัน Git ไม่สามารถตัดสินใจได้ว่าควรเก็บเวอร์ชันไหน ตัวอย่างเช่น คนแรกเปลี่ยน username เป็น “Alice” ขณะที่คนที่สองเปลี่ยนเป็น “Bob”

# Branch A (feature-user-alice) เขียน:
const name = "Alice";
const email = "[email protected]";

# Branch B (feature-user-bob) เขียน:
const name = "Bob";
const email = "[email protected]";

เมื่อพยายาม merge สองบรรทัดนี้ Git จะไม่รู้ว่าชื่อของผู้ใช้ควรเป็น Alice หรือ Bob ดังนั้นจึงขอให้คุณตัดสินใจด้วยตัวเอง

2. ไฟล์ถูกแก้ไขใน branch หนึ่ง และถูกลบใน branch อื่น

Conflict ประเภทนี้เกิดความสับสนเกี่ยวกับว่าควรเก็บไฟล์หรือลบไฟล์ ตัวอย่างเช่น branch A เพิ่มคอลัมน์ใหม่ในไฟล์ config ขณะที่ branch B ลบไฟล์นั้นทั้งหมด Git จะถามว่า “คุณต้องการเก็บไฟล์นี้ที่มีการแก้ไข หรือต้องการลบมันดังที่บรรทัดอื่นทำ?”

# Branch A: เพิ่มบรรทัดใหม่
git add config.json
git commit -m "Add new logging configuration"

# Branch B: ลบไฟล์เดียวกัน
git rm config.json
git commit -m "Remove legacy config file"

# ผลลัพธ์: CONFLICT (delete/modify)

3. Rebase ที่มีการแก้ไขบรรทัดเดียวกัน

เมื่อใช้ git rebase และสองคนแก้ไขบรรทัดเดียวกันในประวัติของ commit ก็จะเกิด conflict ไม่ว่าจะใช้ merge หรือ rebase ก็ตาม

git rebase origin/main
# หากมี commit ที่ขัดแย้ง Git จะหยุดและขอให้คุณแก้ไข

4. Conflict เมื่อ Pull ข้อมูลจาก Remote

เมื่อคุณทำการเปลี่ยนแปลงในเครื่องของคุณและพยายาม pull ข้อมูลล่าสุดจาก server ก็อาจมี conflict เกิดขึ้นหากมีคนอื่นเปลี่ยนแปลงบรรทัดเดียวกัน

# Local changes
git add index.js
git commit -m "Update homepage title"

# Pull from remote
git pull origin main
# CONFLICT! Someone else also updated the same line

วิธีอ่าน Conflict Markers

เมื่อ conflict เกิด Git จะใส่ markers พิเศษลงในไฟล์เพื่อบ่งชี้ส่วนที่มีปัญหา ทำให้คุณสามารถระบุตำแหน่งของ conflict ได้อย่างชัดเจน markers เหล่านี้จะแสดงสองเวอร์ชันของข้อมูล เวอร์ชันอันแรกมาจากสาขาปัจจุบันของคุณ (HEAD) และเวอร์ชันที่สองมาจากสาขาที่คุณพยายามจะ merge

<<<<<<< HEAD
const name = "Alice";  // ของคุณ (current branch)
const email = "[email protected]";
=======
const name = "Bob";    // จากเซิร์ฟเวอร์ (incoming)
const email = "[email protected]";
>>>>>>> feature/new-name
  • <<<<<<< HEAD: แสดงเริ่มต้นของ conflict จากสาขาปัจจุบันของคุณ
  • =======: แยกระหว่างสองเวอร์ชัน ส่วนบนคือของคุณ ส่วนล่างคือเวอร์ชันที่เข้ามา
  • >>>>>>> feature/new-name: แสดงปลายสุดของ conflict และชื่อสาขาที่เข้ามา

วิธี 1: แก้ไขด้วยมือ (Manual Resolution)

วิธีนี้ให้การควบคุมมากที่สุด คุณสามารถเปิดไฟล์ที่มี conflict และลบ conflict markers พร้อมเลือกเนื้อหาที่คุณต้องการ หลังจากแก้ไขไฟล์ทั้งหมด คุณต้อง add และ commit ดังนี้

# 1. เปิดไฟล์ที่มี conflict ด้วยตัวแก้ไข (VS Code, nano, vim)
vim index.js

# 2. ลบ conflict markers และเลือกเวอร์ชันที่คุณต้องการ
# ลบบรรทัด <<<<<<< HEAD, =======, >>>>>>> ออก
# เลือก content ที่ต้องการเก็บ

# 3. บันทึกไฟล์

# 4. Add ไฟล์ที่แก้ไขเสร็จแล้ว
git add index.js

# 5. Commit เพื่อสำเร็จการ merge
git commit -m "Resolve merge conflict in index.js"

# 6. Push ไปยัง remote
git push origin main

วิธี 2: ใช้ Merge Tool (Interactive Resolution)

สำหรับผู้ใช้ที่ต้องการ UI ที่ใช้งานง่าย Git มี mergetool ที่อนุญาตให้ใช้เครื่องมือแบบ visual เมื่อแก้ไข conflicts Tools ที่นิยมใช้ได้แก่ VS Code, Beyond Compare, Meld, และ Kdiff3

# ตั้งค่า mergetool เป็น VS Code (ทำครั้งแรกเท่านั้น)
git config --global merge.tool vscode
git config --global mergetool.vscode.cmd 'code --wait $MERGED'

# เรียกใช้ mergetool เมื่อมี conflict
git mergetool

# VS Code จะเปิดและแสดง conflicts ในรูปแบบ visual
# คุณสามารถคลิกปุ่ม "Accept Current Change", "Accept Incoming Change" หรือ "Accept Both"

# หลังจากแก้ไขเสร็จ ให้ add และ commit
git add .
git commit -m "Resolve merge conflict using VS Code"
git push origin main

วิธี 3: เลือกเวอร์ชันใดเวอร์ชันหนึ่งทั้งหมด

ในบางสถานการณ์ คุณอาจต้องการเก็บเวอร์ชันของคุณทั้งหมด หรือเก็บเวอร์ชันจากเซิร์ฟเวอร์ทั้งหมด Git มี –ours และ –theirs flag สำหรับกรณีเช่นนี้

# เลือกของคุณเองทั้งหมด (current branch/HEAD)
git checkout --ours config.json

# หรือเลือกจากเซิร์ฟเวอร์ทั้งหมด (incoming branch)
git checkout --theirs config.json

# สำหรับไฟล์ที่ถูกลบ: ให้เก็บไฟล์ (resolve as added)
git add filename.txt

# สำหรับไฟล์ที่ถูกลบ: ให้ลบไฟล์ (resolve as deleted)
git rm filename.txt

# หลังจากแก้ไขทั้งหมด
git add .
git commit -m "Resolve conflicts - kept our version"
git push origin main

วิธี 4: ยกเลิก Merge และเริ่มใหม่ (Abort)

หากคุณสบสนหรือต้องการเริ่มใหม่ คุณสามารถยกเลิกการ merge ทั้งหมดและกลับไปสู่สภาพเดิมก่อนเริ่ม merge

# ยกเลิก merge และกลับไปยังสภาพเดิม
git merge --abort

# หรือถ้าใช้ rebase
git rebase --abort

วิธีป้องกัน Merge Conflict

แม้ว่า merge conflict บางครั้งหลีกเลี่ยงไม่ได้ แต่การปฏิบัติตามวิธีการที่ดีจะช่วยลดจำนวนและความซับซ้อนของ conflicts ได้อย่างมาก ต่อไปนี้เป็นกลยุทธ์ที่ช่วยให้คุณสามารถทำงานแบบทีมได้อย่างมีประสิทธิภาพบน Cloud VPS

  • Pull บ่อยๆ: ดึงข้อมูลล่าสุดจาก remote repository ทุกวันหรือหลายครั้งต่อวัน ยิ่ง pull บ่อยเท่าไหร่ ยิ่งลดการแตกต่างของ code ได้มากขึ้น และจะมี conflicts ที่เล็กน้อยขึ้น
  • Branch ขนาดเล็ก: สร้าง branch ที่เล็กและมี scope ที่ชัดเจน แทนที่จะสร้าง branch ขนาดใหญ่ที่สำเร็จภายในหลายสัปดาห์ Branch ขนาดเล็กใช้เวลา merge เร็วกว่า และมีสัญชาติด้อยกว่า
  • Communicate กับทีม: บอกทีมว่าคุณกำลังแก้ไขไฟล์ใด หลีกเลี่ยงการแก้ไขไฟล์เดียวกันในเวลาเดียวกันกับสมาชิกคนอื่น
  • Code Review ด้วย PR: สร้าง Pull Request แล้วขอให้ทีมทำการ review ก่อน merge เข้า main branch คนอื่นอาจเห็นปัญหาหรือ conflicts ที่ขึ้นมาก่อนที่มันจะเป็นปัญหาใหญ่
  • หลีกเลี่ยง Large Files: อย่าเก็บไฟล์ขนาดใหญ่ (เช่น binary files) ใน Git ถ้าเป็นไปได้ ใช้ Git LFS สำหรับไฟล์ขนาดใหญ่ แทน
  • ใช้ Merge Strategy: พิจารณาการใช้ squash merge หรือ rebase merge แทน three-way merge เพื่อให้ history ของ commit สะอาดขึ้น
  • Automated Testing: ตั้งค่า CI/CD pipeline เพื่อให้รัน tests โดยอัตโนมัติ ซึ่งจะช่วยตรวจหา conflicts ที่อาจเกิดขึ้นได้
  • Branch Protection: ตั้งค่า branch protection rules ใน GitHub หรือ GitLab เพื่อบังคับให้ PR ผ่านการ review และ tests ก่อน merge

ตัวอย่าง Workflow ที่ดีเพื่อลด Conflicts

# ขั้นตอนที่ 1: Pull ข้อมูลล่าสุดก่อนเริ่มงาน
git checkout main
git pull origin main

# ขั้นตอนที่ 2: สร้าง branch ใหม่ที่มี scope ชัดเจน
git checkout -b feature/add-user-authentication

# ขั้นตอนที่ 3: ทำงานและ commit เป็นระยะ
git add .
git commit -m "feat: add login form"

# ขั้นตอนที่ 4: Pull ข้อมูลล่าสุดอีกครั้ง (ในวันหรือหลังจาก commit)
git pull origin main

# ขั้นตอนที่ 5: ถ้ามี conflict ให้แก้ไข
# (แก้ไขไฟล์ที่ conflict)
git add .
git commit -m "Resolve merge conflict"

# ขั้นตอนที่ 6: Push branch และสร้าง PR
git push origin feature/add-user-authentication

# ขั้นตอนที่ 7: ขอให้ทีมทำการ review บน GitHub/GitLab

เครื่องมือช่วยลด Conflicts

นอกจากการปฏิบัติตามกลยุทธ์ที่ดี ยังมีเครื่องมือที่สามารถช่วยได้

  • GitLens (VS Code Extension): แสดงประวัติการแก้ไขของแต่ละบรรทัด ช่วยให้เข้าใจว่าใครแก้ไขอะไรและเมื่อไร
  • Conventional Commits: ใช้รูปแบบ commit message ที่มาตรฐาน เช่น “feat:”, “fix:”, “refactor:” เพื่อให้ history ของ commits ชัดเจน
  • Pre-commit Hooks: ตั้งค่า hooks เพื่อให้ run tests และ linters อัตโนมัติก่อน commit เพื่อจับปัญหาก่อนเวลามาถึง
  • GitHub Desktop หรือ GitKraken: GUI tools ที่ช่วยจัดการ conflicts ได้ง่ายกว่า command line สำหรับผู้ใช้ที่ชอบใช้ interface

สรุป

เข้าใจสาเหตุของ merge conflict และรู้วิธีแก้ไขเป็นทักษะที่สำคัญสำหรับนักพัฒนา Git merge conflict ไม่ใช่ข้อผิดพลาด แต่เป็นสัญญาณที่บ่อบอกให้คุณตัดสินใจว่าควรเลือกโค้ดเวอร์ชันไหน โดยการปฏิบัติตามวิธีการที่ดี เช่น pull บ่อยๆ สร้าง branch ขนาดเล็ก สื่อสารกับทีม และทำการ code review คุณสามารถลดจำนวนและความซับซ้อนของ conflicts ได้อย่างมาก การพัฒนา software บน Cloud Hosting ของ ผู้ให้บริการโฮสติ้ง จะเป็นประสบการณ์ที่ราบรื่นและมีประสิทธิภาพมากขึ้น