ลืม Pull ก่อน Push เกิดอะไรขึ้น? วิธีแก้ไข Rejected Push

ในสถานการณ์ที่พบบ่อยจากการทำงานร่วมกัน คุณลืม Pull ข้อมูลล่าสุดจากเซิร์ฟเวอร์ แล้วพยายาม Push งานของคุณขึ้นไป ผลที่ได้คือ Git จะปฏิเสธ Push ของคุณด้วยข้อความ error เพราะมีการเปลี่ยนแปลง (diverged histories) บนเซิร์ฟเวอร์แล้ว เรียนรู้วิธีแก้ไข Rejected Push และป้องกันไม่ให้เกิดในอนาคต

เมื่อคุณลืม Pull ก่อน Push จะเกิดอะไรขึ้น

ในการพัฒนา application บน Cloud VPS หรือสภาพแวดล้อมใดๆ ที่มีทีมพัฒนาหลายคน การลืม Pull ก่อน Push เป็นปัญหาที่พบบ่อยมาก สาเหตุมาจากการที่คุณทำงานในท้องถิ่น (local) ไม่รู้ว่ามีคนอื่นเปลี่ยนแปลงชิ้นเดียวกันแล้ว

สถานการณ์ทั่วไป

  • คุณเริ่มทำงาน โดย clone จาก branch main
  • คุณเขียน code และ commit ลงตัวเอง
  • ในขณะเดียวกัน คนอื่นเพิ่ม code และ push ขึ้นไป
  • คุณพยายาม push code ของตัวเอง
  • Git จะปฏิเสธ push เพราะมี conflict

ทำไมจึงเกิด Rejected Push

Git ปฏิเสธการ Push เพื่อป้องกันการสูญเสียข้อมูล เมื่อ branch บนเซิร์ฟเวอร์มี commit ที่ไม่มีอยู่ในเครื่องของคุณ Git ไม่สามารถเพิ่ม commit ของคุณได้โดยตรง เพราะจะทำให้ history ไม่สอดคล้องกัน และอาจสูญเสียการเปลี่ยนแปลงของคนอื่น

ข้อความ Error ที่คุณจะเห็น

$ git push

! [rejected]        main -> main (fetch first)
error: failed to push some refs to 'https://github.com/user/repo.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

ข้อความนี้บอกอย่างชัดเจนว่า branch ของคุณไม่ทันเทียบกับ remote คุณต้อง pull ก่อน

วิธีแก้ไข #1: git pull (Merge Strategy)

วิธีที่ง่ายที่สุดและมักใช้บ่อยที่สุดคือการ pull ข้อมูลล่าสุดจากเซิร์ฟเวอร์ก่อน ซึ่ง Git จะทำการ merge commit ของเซิร์ฟเวอร์เข้ากับงานของคุณ

git pull

คำสั่งนี้จะ:

  • Fetch commit ล่าสุดจาก remote
  • Merge โดยอัตโนมัติ (ถ้าไม่มี conflict)
  • สร้าง merge commit เพื่อบอกว่า code ถูก merge

หากเกิด Merge Conflict

ถ้า git pull ทำให้เกิด conflict คุณต้องแก้ไขให้เรียบร้อยก่อน จากนั้นจึง push ได้

# Git จะบอกว่าไฟล์ไหนมี conflict
# เปิดไฟล์นั้นและแก้ไข markers:
# <<<<<<< HEAD (your changes)
# ||||||| merged common ancestors
# =======
# >>>>>>> branch name (their changes)

# หลังแก้ไขแล้ว:
git add .
git commit -m "Merge remote changes"
git push

วิธีแก้ไข #2: git pull –rebase (Linear History)

ถ้าคุณต้องการให้ history สะอาดและเป็นเส้นตรง (linear) ให้ใช้ –rebase flag แทน วิธีนี้จะย้าย commit ของคุณไปไว้หลัง commit ของเซิร์ฟเวอร์ แทนที่จะสร้าง merge commit

git pull --rebase

ผลลัพธ์ที่ได้ คือ history จะดูเหมือนว่าคุณเคย pull มาแล้วจึงค่อย commit เพิ่มเติม

ข้อดี-ข้อเสีย: Merge vs Rebase

Merge (git pull)

  • ข้อดี: เก็บประวัติการทำงานทั้งหมด การมอง history จะเห็นว่ามีการ merge เกิดขึ้น
  • ข้อดี: ปลอดภัยกว่า ไม่มีการเปลี่ยน commit history
  • ข้อเสีย: History จะมี merge commits เยอะ ดูสับสนเล็กน้อย
  • ใช้ดีสำหรับ: การทำงานร่วมกันในทีม สาขา main ของ project

Rebase (git pull –rebase)

  • ข้อดี: History เรียบร้อย เป็นเส้นตรง ดูสะอาด
  • ข้อดี: ง่ายต่อการ bisect หา commit ที่มีบัก
  • ข้อเสีย: Rewriting history อาจเป็นปัญหาถ้าใช้ร่วมกับคนอื่น
  • ใช้ดีสำหรับ: Feature branch ของตัวเอง ก่อนสร้าง PR

วิธีแก้ไข #3: ตั้งค่า Default Strategy

ถ้าคุณต้องการให้ git pull ใช้ rebase โดยค่าเริ่มต้น ตั้งค่า git config ได้ดังนี้

# สำหรับ branch ปัจจุบัน
git config pull.rebase true

# สำหรับ machine ทั้งหมด
git config --global pull.rebase true

หลังจากตั้งค่านี้ git pull จะใช้ rebase ตามค่าเริ่มต้น

วิธีแก้ไข #4: Fetch แล้ว Merge เอง

ถ้าต้องการควบคุม pull process อย่างละเอียด คุณสามารถแยก fetch และ merge ได้

# ขั้นที่ 1: Fetch ข้อมูลจาก remote
git fetch origin

# ขั้นที่ 2: ดูว่า main branch บน remote เปลี่ยนแปลงแล้ว
git log --oneline main..origin/main

# ขั้นที่ 3: Merge หลังจากที่คุณตัดสินใจแล้ว
git merge origin/main

# หรือ rebase
git rebase origin/main

วิธีป้องกันไม่ให้ลืม Pull

  • Pull ทุกครั้งก่อนเริ่มทำงาน – เป็นนิสัยที่ดี เมื่อมาทำงานวันใหม่ ต้อง pull ก่อน
  • Pull ก่อน Push เสมอ – ก่อน push งาน ต้องแน่ใจว่ามี update ใหม่จาก remote
  • ตั้งค่า git config –global pull.rebase true – เพื่อให้ default เป็น rebase
  • ใช้ git status บ่อยๆ – ดูสถานะ branch ปัจจุบันเสมอ
  • ใช้ Pre-push Hook – สร้าง script ที่ fetch อัตโนมัติก่อน push
  • อ่าน Output ของ git command – Git บอกให้คิดครั้ง ถ้าต้อง pull

ตัวอย่าง Real-World Scenario

มาดูสถานการณ์จริง เพื่อให้เข้าใจดีขึ้น สมมุติว่าคุณกำลังพัฒนา feature ใหม่ บน Cloud VPS project

# สถานการณ์:
# 1. คุณ checkout feature branch สัปดาห์ที่แล้ว
# 2. เขียน code และ commit
# 3. พยายาม push วันนี้
# 4. Git ปฏิเสธ

# ขั้นที่ 1: ลองดูว่า branch ล่าสุดเป็นอะไร
$ git status
On branch feature/new-auth-system
Your branch is behind 'origin/feature/new-auth-system' by 3 commits.

# ขั้นที่ 2: pull latest changes
$ git pull
Updating a1b2c3d..f8e7d6c
Fast-forward
 src/auth.js | 12 ++++++++++
 src/db.js   | 5 ++---
 2 files changed, 14 insertions(+), 3 deletions(-)

# ขั้นที่ 3: ลองดู log
$ git log --oneline -5
f8e7d6c Fix authentication bug
a1b2c3d Add new auth flow
z9x8w7v Update dependencies

# ขั้นที่ 4: Push งานของคุณ
$ git push
Counting objects: 5, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 400 bytes | 0 bytes/s, done.

# Success!
To https://github.com/user/project.git
   f8e7d6c..a1b2c3d  feature/new-auth-system -> feature/new-auth-system

Tips สำหรับทีมพัฒนา

  • Communicate ในทีม – บอก teammates เมื่อคุณ push เพื่อให้พวกเขา pull
  • ใช้ Feature Branches – แต่ละ feature ควรมี branch เอง ลดการ conflict
  • Keep Branches Short-lived – Merge เร็ว ลด conflict risk
  • ทดสอบก่อน Push – ตัวเข้าใจว่า code ทำงานก่อนไป push
  • Code Review Process – ใช้ Pull Request สำหรับ review ก่อน merge
  • CI/CD Pipeline – เพื่อให้ automatic testing

สรุป: Rejected Push ไม่ใช่ปัญหา

ความเข้าใจในเรื่องนี้จะช่วยให้คุณทำงานกับ Git ได้มั่นใจมากขึ้น Rejected Push คือการป้องกันของ Git เพื่อให้คุณไม่สูญเสียงาน ในขณะที่คุณมีงาน Pull ก่อนเสมอ ไม่ว่าจะใช้ merge หรือ rebase ทั้งสองวิธีมีข้อดี เลือกตามที่เหมาะกับทีมของคุณ