กลับไปที่บล็อก

Git undo last commit: ย้อนคอมมิตโดยไม่เสียโค้ด

3 กันยายน 2569

TL;DR: จะย้อนคอมมิตสุดท้ายแต่เก็บการเปลี่ยนแปลงไว้ ให้รัน git reset --soft HEAD~1 (การเปลี่ยนแปลงยังอยู่ใน staging) หรือ git reset HEAD~1 (การเปลี่ยนแปลงอยู่ใน working tree) ถ้าคอมมิตถูกพุชไปแล้ว ให้รัน git revert HEAD แทน — มันสร้างคอมมิตใหม่ที่ยกเลิกคอมมิตเดิมโดยไม่แตะประวัติ สามคำสั่งนี้ครอบคลุมแทบทุกสถานการณ์ “คอมมิตเร็วเกินไป” ที่เหลือของบทความจะไล่แต่ละกรณีด้วยคำสั่ง copy-paste อธิบายความต่างของ --soft, --mixed และ --hard พร้อมวิธีกู้คืนเมื่ออะไรก็ตามผิดพลาด ทุกคำสั่งด้านล่างใช้ได้กับ Git เวอร์ชันใหม่บน Linux, macOS และ Windows

จะย้อนคอมมิตสุดท้ายโดยเก็บการเปลี่ยนแปลงไว้ได้อย่างไร?

สถานการณ์ที่พบบ่อยที่สุด: คอมมิตไปแล้วค่อยเจอพิมพ์ผิด ไฟล์ตกหล่น หรือนึกได้ว่าการเปลี่ยนแปลงนี้ควรอยู่อีกคอมมิต ยังไม่ได้พุชอะไรเลย ย้อนคอมมิตแล้วคืนทุกอย่างกลับที่เดิม:

# คอมมิตหายจากประวัติ — การเปลี่ยนแปลงกลับเข้า staging area
git reset --soft HEAD~1

# การเปลี่ยนแปลงกลับไปอยู่ใน working tree (ยังไม่ staged) แทน
git reset HEAD~1

HEAD~1 หมายถึง “หนึ่งคอมมิตก่อนจุดที่ HEAD ชี้อยู่” หลังจากคำสั่งใดก็ตาม ไฟล์บนดิสก์ของคุณไม่ถูกแตะ — สิ่งที่ขยับมีแค่พอยเตอร์ของ branch เช็กด้วย git status: ถ้าใช้ --soft การเปลี่ยนแปลงจะอยู่ในสถานะ staged พร้อมคอมมิตซ้ำพร้อมแก้ไข ถ้าไม่ใส่ flag จะเป็น unstaged เพื่อให้แก้ไขเพิ่มก่อนได้อย่างอิสระ

นิสัยที่ปลอดภัยกว่า เมื่อคุณแค่อยากเพิ่มไฟล์เข้าคอมมิตล่าสุด คือไม่ต้องย้อนเลย:

git add forgotten-file.txt
git commit --amend --no-edit

--amend แทนที่คอมมิตล่าสุดในที่เดิม (ตรงนี้ไม่แก้ข้อความคอมมิต) ข้อควรจำ: amend คอมมิตที่ พุชไปแล้ว คือการเขียนทับประวัติ — รายละเอียดอยู่ด้านล่าง

ความต่างของ —soft, —mixed และ —hard คืออะไร?

ส่วนที่ควรจำ เพราะ flag ตัดสินว่าการเปลี่ยนแปลงของคุณจะไปจบที่ไหน — และจะหายได้หรือไม่:

Flagยกเลิกคอมมิต?ไฟล์บนดิสก์ยัง staged?ใช้เมื่อ
--softใช่เก็บไว้ใช่คอมมิตซ้ำพร้อมแก้เล็กน้อย
--mixed (ค่าเริ่มต้น)ใช่เก็บไว้ไม่จัดกลุ่มการเปลี่ยนแปลงใหม่ stage แบบเลือกทีละไฟล์
--hardใช่ถูกลบทิ้งงานทั้งหมด
git revertไม่ (เป็นคอมมิตใหม่)เก็บไว้ยกเลิกคอมมิตที่พุชไปแล้ว

--hard เป็นตัวเดียวที่อันตราย: มันทิ้งทั้งคอมมิต และ การเปลี่ยนแปลง ก่อน reset --hard ทุกครั้ง ให้เก็บงานไว้ใน stash หรือ branch ก่อน:

git branch backup-before-reset   # ประกันราคาถูก
git reset --hard HEAD~1          # คอมมิตและการเปลี่ยนแปลงหายทั้งคู่

ถ้ารันเวอร์ชันอันตรายไปแล้ว ยังไม่ใช่จุดจบ — git reflog จำได้ว่า HEAD เคยไปที่ไหน:

git reflog                       # หา hash ของคอมมิตที่เสียไป
git reset --hard HEAD@{1}        # หรือ: git reset --hard <hash>

reflog เก็บคอมมิตที่ลอยอยู่ไว้ราว 90 วันโดยดีฟอลต์ ดังนั้น “ผม hard-reset ผิดมือ” แทบกู้คืนได้เสมอ ถ้ารีบก่อนถูกเก็บขยะ

ถ้าพุชคอมมิตไปแล้วล่ะ?

ถ้าคอมมิตอยู่บน branch ที่ใช้ร่วมกัน (อะไรก็ตามที่ไม่ใช่ feature branch ส่วนตัวของคุณ) อย่าเขียนทับประวัติ ให้ใช้ git revert ซึ่งคำนวณการเปลี่ยนแปลงตรงข้ามแล้วคอมมิตลงไป:

git revert HEAD
git push

ทุกคนที่ pull จะได้แค่คอมมิตใหม่ที่ลบการเปลี่ยนแปลงของคอมมิตเก่า ไม่มี force-push ไม่มีเพื่อนร่วมงานพัง ถ้าต้องยกเลิกคอมมิตหลายตัว ให้ revert เป็นช่วง: git revert --no-commit HEAD~3..HEAD && git commit

ทางเลือก — git reset --hard HEAD~1 && git push --force-with-lease — ยอมรับได้เฉพาะบน branch ที่ไม่มีใครอื่นใช้ และ --force-with-lease (ไม่ใช่ --force เปล่า ๆ) คือรูปแบบเดียวที่ปลอดภัย เพราะจะปฏิเสธถ้ามีคนพุชแทรกมาก่อน force-push บน branch ส่วนกลางคือวิธีที่ทีมเสียคอมมิต และ log ของ CI เลิกแมตช์กับ checkout ของใครสักคนอย่างน่างง

จะย้อนคอมมิตล่าสุดแต่เก็บไว้ใช้ทีหลังได้อย่างไร?

บางทีคอมมิตนั้นเป็นงานดีที่อยู่ผิดที่ — ผิด branch หรือเร็วไป แทนที่จะย้อน ก็ย้ายมัน:

git branch stash-commit          # จอดคอมมิตไว้บน branch ใหม่
git reset --hard HEAD~1          # แล้วค่อยเก็บกวาด branch ปัจจุบัน

หรือหยิบเฉพาะคอมมิตนั้นไปอีก branch โดยไม่แตะ branch ปัจจุบัน:

git cherry-pick <hash>           # ขณะอยู่บน branch ปลายทาง

ระหว่าง reset --soft, cherry-pick และ revert ไม่มีคอมมิตใดที่ย้ายหรือลบล้างไม่ได้ — เทคนิคคือเลือกให้ออกว่าอยาก “ย้าย” หรือ “ยกเลิก” ก่อนยื่นมือไปหา flag

ควรใช้การย้อนแบบไหน? คู่มือตัดสินใจฉบับเร่งรัด

  1. ยังไม่พุช อยากแก้แล้วคอมมิตใหม่git reset --soft HEAD~1
  2. ยังไม่พุช อยาก stage เลือกไฟล์git reset HEAD~1 (mixed)
  3. อยากให้การเปลี่ยนแปลงหายไปเลยgit reset --hard HEAD~1 (reflog ยังรู้ ถ้าคุณใจเสีย)
  4. พุชไปแล้วบน branch ร่วมgit revert HEAD
  5. คอมมิตควรอยู่อีก branchcherry-pick ไม่ต้องย้อน

เคล็ดลับปฏิบัติการข้อสุดท้าย: ถ้าคอมมิตพังมันลอดไปถึงเซิร์ฟเวอร์จริง ๆ — deploy hook ที่พัง หรือ CI runner ทำตัวประหลาดหลัง force-push — จุดหมายถัดไปของการตามคือ log ของเครื่อง ไม่ใช่ Git บนเครื่องที่ใช้ systemd ใด ๆ journalctl -u <service> -n 100 จะบอกได้ละเอียดว่าอะไรรันไปแล้วเมื่อไหร่ ใน สรุปคำสั่ง journalctl ของเรามีรูปแบบคำสั่ง copy-paste ครบ และถ้าเวิร์กโฟลว์ของคุณมีเครื่องมือ LLM ในเครื่องไว้รีวิว diff เราเคยเทียบสองตัวหลักไว้แล้วใน Ollama vs LM Studio

— mrsaynothing

— mrsaynothing

บันทึกหน้างานเรื่อง AI, Linux และ self-hosted

คุยต่อโพสต์นี้บน dev.to dev.to ↗

รับวิธีแก้ฉบับถัดไปทางอีเมล

อีเมลหนึ่งฉบับต่อหนึ่งโพสต์ แก้เสร็จแล้วไปต่อ

self-hosted · ไม่มีบุคคลที่สาม · ยกเลิกได้ในคลิกเดียว

นี่คืออะไร?

สรุปคำสั่ง journalctl: ดู กรอง และติดตาม log บน Linux

ถ้าอ่านแล้วชอบ — ผมสร้างงานแบบนี้เป็นอาชีพ จ้างผม