Workshop: แก้ไข Merge Conflict ด้วยมือ ฝึกจนชำนาญ

Workshop: แก้ไข Merge Conflict ด้วยมือ ฝึกจนชำนาญ

บทความนี้จะสอนวิธีสร้าง Merge Conflict ขึ้นมาจำลองเหตุการณ์จริง และฝึกการแก้ไขอย่างเป็นขั้นเป็นตอน เพื่อให้คุณและทีมพัฒนาเข้าใจกลไกของ Git Conflict อย่างลึกซึ้ง และสามารถจัดการกับสถานการณ์ที่ซับซ้อนได้อย่างมั่นใจ การแก้ Merge Conflict ที่ถูกต้องเป็นทักษะสำคัญ โดยเฉพาะเมื่อทีมขนาดใหญ่ทำงานบน repository เดียวกัน หรือเมื่อพัฒนาหลาย feature พร้อมกันบน ผู้ให้บริการโฮสติ้ง Cloud VPS

Merge Conflict คืออะไร?

Merge Conflict เกิดขึ้นเมื่อ Git ไม่สามารถรวม (merge) code จากสอง branch ได้โดยอัตโนมัติ นี่มักจะเกิดขึ้นเมื่อ:

  • สองคนแก้ไขไฟล์เดียวกันในส่วนเดียวกัน
  • คนหนึ่งลบไฟล์ แต่อีกคนแก้ไขไฟล์นั้น
  • ทั้งสอง branch มีการเปลี่ยนแปลง โครงสร้างไฟล์ที่แตกต่างกัน
  • Merge จาก branch หนึ่งไปยัง branch อีกอันและมีการชนกันของ code

การเข้าใจและแก้ไข conflict อย่างถูกต้องจะช่วยรักษา code quality และป้องกันข้อผิดพลาดที่อาจเกิดจากการ merge ผิดพลาด

ขั้นตอนที่ 1: เตรียมสภาวะแวดล้อม

ก่อนสร้าง conflict scenario เราต้องมี repository พร้อมใช้งาน:

# สร้าง directory ใหม่
mkdir git-conflict-workshop
cd git-conflict-workshop

# Initialize repository
git init

# ตั้งค่า user สำหรับ commits
git config user.name "John Developer"
git config user.email "[email protected]"

# สร้างไฟล์เบื้องต้น
echo "# Merge Conflict Workshop" > README.md
git add README.md
git commit -m "Initial commit"

# สร้าง main branch (ตั้ง main เป็น default)
git branch -M main

ขั้นตอนที่ 2: สร้าง Conflict Scenario – สองคนแก้ไขไฟล์เดียวกัน

สถานการณ์จำลอง: Alice กำลังพัฒนา feature A บน branch หนึ่ง ขณะที่ Bob กำลังพัฒนา feature B บน branch อื่น และทั้งคู่แก้ไขไฟล์เดียวกัน

# สร้างไฟล์ที่จะถูก conflict
cat > config.js << 'EOF'
module.exports = {
  apiUrl: 'https://api.example.com',
  port: 3000,
  debug: false
};
EOF

git add config.js
git commit -m "Add configuration file"

# ตอนนี้เรามี config.js บน main branch

ขั้นตอนที่ 3: สร้าง Branch A - Feature ของ Alice

Alice เริ่มพัฒนา feature ใหม่ ขณะที่บาง feature ยังไม่เสร็จบน main:

# Alice สร้าง branch สำหรับ feature ของเธอ
git checkout -b feature/alice-auth

# Alice แก้ไข config.js เพื่อเพิ่ม authentication settings
cat > config.js << 'EOF'
module.exports = {
  apiUrl: 'https://api.example.com',
  port: 3000,
  debug: false,
  auth: {
    enabled: true,
    jwtSecret: 'secret-key-alice',
    tokenExpiry: 3600
  }
};
EOF

# Alice commit การเปลี่ยนแปลง
git add config.js
git commit -m "Add authentication configuration by Alice"

echo "Alice finished her feature"

ขั้นตอนที่ 4: สร้าง Branch B - Feature ของ Bob

ในเวลาเดียวกัน Bob ก็เริ่มพัฒนา feature อื่น จาก main branch เดิม:

# กลับไปที่ main branch (ยังไม่มี changes ของ Alice)
git checkout main

# Bob สร้าง branch ของเขาเอง
git checkout -b feature/bob-logging

# Bob แก้ไข config.js เพื่อเพิ่ม logging configuration
cat > config.js << 'EOF'
module.exports = {
  apiUrl: 'https://api.example.com',
  port: 3000,
  debug: false,
  logging: {
    level: 'info',
    format: 'json',
    output: '/var/log/app.log'
  }
};
EOF

# Bob commit การเปลี่ยนแปลง
git add config.js
git commit -m "Add logging configuration by Bob"

echo "Bob finished his feature"

ขั้นตอนที่ 5: สร้าง Conflict ด้วย Merge

ตอนนี้เราพร้อมที่จะสร้าง conflict โดยการ merge branch ทั้งสอง:

# อันดับแรก merge feature ของ Alice ไปที่ main
git checkout main
git merge feature/alice-auth
# ขณะนี้ main มีการ authenticate config ของ Alice

# ตอนนี้พยายาม merge feature ของ Bob
git merge feature/bob-logging

# Git จะแจ้ง CONFLICT!
# Auto-merging config.js
# CONFLICT (content): Merge conflict in config.js
# Automatic merge failed; fix conflicts and then commit the result.

ขั้นตอนที่ 6: ตรวจสอบ Conflict Markers

เมื่อ conflict เกิดขึ้น Git จะแทรก conflict markers ลงในไฟล์ที่ชนกัน ให้ดูไฟล์ว่ามีลักษณะอย่างไร:

# ดูสถานะ conflict
git status

# Output:
# On branch main
# You have unmerged paths.
#   (fix conflicts and run "git commit")
# both modified:   config.js

# ดูเนื้อหาของไฟล์ที่ conflict
cat config.js

# Output จะมี conflict markers:
# module.exports = {
#   apiUrl: 'https://api.example.com',
#   port: 3000,
#   debug: false,
# <<<<<<< HEAD (main - Alice's changes)
#   auth: {
#     enabled: true,
#     jwtSecret: 'secret-key-alice',
#     tokenExpiry: 3600
#   }
# =======
#   logging: {
#     level: 'info',
#     format: 'json',
#     output: '/var/log/app.log'
#   }
# >>>>>>> feature/bob-logging
# };

# ส่วน HEAD แสดง changes ใน main (Alice)
# ส่วน feature/bob-logging แสดง changes ใน Bob branch

ขั้นตอนที่ 7: แก้ไข Conflict ด้วยมือ

ตอนนี้เรามีสามตัวเลือก ในการแก้ conflict:

  • ใช้ ours: เก็บ code ของ main (Alice's auth)
  • ใช้ theirs: เก็บ code ของ feature branch (Bob's logging)
  • ผสม (merge): รวม code ทั้งสองอย่างอย่างเหมาะสม

ในกรณีนี้ เราควรรวม authentication และ logging ทั้งคู่ เพราะเป็นไฟล์ configuration ที่ไม่ขัดแย้งกันโดยพื้นฐาน:

# แก้ไข config.js ด้วย text editor ที่คุณชอบ
# เนื้อหาที่ถูกต้อง:
cat > config.js << 'EOF'
module.exports = {
  apiUrl: 'https://api.example.com',
  port: 3000,
  debug: false,
  auth: {
    enabled: true,
    jwtSecret: 'secret-key-alice',
    tokenExpiry: 3600
  },
  logging: {
    level: 'info',
    format: 'json',
    output: '/var/log/app.log'
  }
};
EOF

echo "Conflict resolved manually!"

# ตรวจสอบไฟล์ว่า conflict markers หายไปแล้ว
cat config.js

ขั้นตอนที่ 8: Stage และ Commit

หลังจากแก้ไข conflict เรามีหน้าที่ที่จะ stage ไฟล์และ commit merge:

# Stage ไฟล์ที่แก้ไข
git add config.js

# ตรวจสอบสถานะ
git status
# Output:
# On branch main
# All conflicts fixed but you are still merging.
#   (use "git commit" to conclude merge)
# Changes to be committed:
#   modified:   config.js

# สร้าง merge commit
git commit -m "Merge feature/bob-logging into main: combine auth and logging config"

# Git จะสร้าง merge commit และปิด merge process
echo "Merge completed successfully!"

ขั้นตอนที่ 9: ตรวจสอบ Git Log และ History

ดูประวัติของ merge ที่เราเพิ่งทำให้เสร็จสิ้น:

# ดู git log แบบ graph เพื่อเห็นความสัมพันธ์ของ branches
git log --oneline --all --graph

# Output (ตัวอย่าง):
# *   abc1234 Merge feature/bob-logging into main: combine auth and logging config
# |\\  
# | * def5678 Add logging configuration by Bob
# * | ghi9012 Add authentication configuration by Alice
# |/   
# * jkl3456 Add configuration file
# * mno7890 Initial commit

# ดู diff สำหรับ merge commit
git show abc1234

# ดู commits ที่อยู่บน branch ใดบ้าง
git branch -v
git branch --contains abc1234

ขั้นตอนที่ 10: ทำความเข้าใจเหล่าคำสั่ง Git Merge Strategy

Git มีหลายวิธีในการ merge ไฟล์ ขึ้นอยู่กับสถานการณ์:

# 1. Recursive merge (default for most cases)
git merge feature-branch

# 2. Ours strategy: เก็บ code จาก current branch ทั้งหมด
git merge -s ours feature-branch

# 3. Theirs strategy: เก็บ code จาก feature branch ทั้งหมด
git merge -X theirs feature-branch

# 4. Octopus merge: merge หลาย branches พร้อมกัน
git merge branch1 branch2 branch3

# 5. Resolve specific conflict ด้วย manual edit
# ใช้ git checkout --ours หรือ --theirs
git checkout --ours config.js  # ใช้ version ของ main
git checkout --theirs config.js  # ใช้ version ของ feature branch

การแก้ Conflict ด้วยเครื่องมือสนับสนุน

สำหรับ conflict ที่ซับซ้อน คุณสามารถใช้ merge tools ต่างๆ:

# ตั้งค่า merge tool (ตัวอย่างใช้ vimdiff)
git config --global merge.tool vimdiff

# เปิด merge tool อย่างอินเทอร์แอกทีฟ
git mergetool

# Merge tools ที่นิยม:
# - vimdiff (command line)
# - meld (GUI)
# - kdiff3 (GUI)
# - Beyond Compare (commercial)
# - Visual Studio Code (built-in)

# ในเทอร์มินัล เปิด VS Code merge tool
git config --global merge.tool vscode
git config --global mergetool.vscode.cmd 'code --wait $MERGED'

Best Practices ในการหลีกเลี่ยง Conflict

  • Commit บ่อย: ยิ่งกระจาย commits ให้มากขึ้น ยิ่งลดโอกาสของ conflict
  • Pull ก่อน Push: ลดความเสี่ยงของ conflicting changes ด้วยการ pull ก่อน
  • แยก Features: อย่าแก้ไขไฟล์เดียวกันจากคนต่างกันในเวลาเดียวกัน
  • Code Review: review PR ก่อน merge เพื่อพบ conflict เร็ว
  • Rebase ตามสมควร: ใช้ rebase สำหรับ branches ส่วนตัว เพื่อลดจำนวน merge commits
  • ใช้ Git Flow: ทำให้การจัดการ branches เป็นระบบ บน ผู้ให้บริการโฮสติ้ง VPS

Conflict Resolution Checklist

  • ทำความเข้าใจว่า conflict มาจากไหน ด้วยการดู git log
  • ดูไฟล์ที่ conflict และเข้าใจ conflict markers
  • ตัดสินใจว่าจะเลือก HEAD, incoming changes หรือผสม
  • แก้ไขไฟล์ด้วยมือให้ถูกต้องตามตรรกะทางธุรกิจ
  • ทดสอบ code ที่แก้ไข เพื่อแน่ใจว่าไม่มี syntax error
  • Stage ไฟล์ด้วย git add
  • สร้าง commit message ที่บรรยายการแก้ conflict
  • Push ไปยัง remote repository

เมื่อ Conflict ยากเกินไป: Abort Merge

ถ้าคุณสับสน หรือต้องการเริ่มใหม่ คุณสามารถยกเลิก merge process:

# ยกเลิก merge process ทั้งหมด (กลับไปสถานะปกติ)
git merge --abort

# หรือ reset merge
git reset --hard HEAD

# ทีนี้คุณสามารถลองใหม่หรือเปลี่ยนแนวทาง

การ Deploy ของ Code ที่ Merged

เมื่อ merge สำเร็จบน repository ท้องถิ่น คุณต้อง push ไปยัง remote และทำการ deploy บน ผู้ให้บริการโฮสติ้ง Cloud VPS:

# Push merge commit ไปยัง remote
git push origin main

# หรือ push ทั้งหมด branches
git push origin --all

# ใน VPS ของ ผู้ให้บริการโฮสติ้ง ให้ pull code ล่าสุด
# (เข้า VPS ผ่าน SSH)
ssh user@vps-ip

cd /var/www/app
git pull origin main

# หรือใช้ webhook เพื่อ auto-deploy
# ตัวอย่าง GitHub Actions webhook deploy
# ดู Deploy เว็บจาก GitHub สู่ VPS

สรุป

Workshop นี้ให้ความรู้ลึกในการแก้ Merge Conflict ด้วยมือ ซึ่งเป็นทักษะสำคัญสำหรับทีมพัฒนาที่ทำงานกับ Git ทุกวัน ขณะที่การแก้ conflict อาจดูซับซ้อนในตอนแรก แต่การฝึกซ้ำอีกครั้งจะทำให้คุณสามารถจัดการกับสถานการณ์ที่ซับซ้อนได้อย่างมีประสิทธิภาพ การเข้าใจถึงกลไกของ Git merge ยังช่วยให้คุณออกแบบ branching strategy ที่ดีขึ้น ซึ่งจะลดจำนวนของ conflicts ลงไปในอนาคต การ manage repository อย่างถูกต้องบน ผู้ให้บริการโฮสติ้ง Cloud VPS นั้นต้องอาศัยทักษะนี้เป็นอย่างมาก