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 นั้นต้องอาศัยทักษะนี้เป็นอย่างมาก
