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

Git revert vs reset: ตัวไหนช่วยประวัติคุณไว้ได้

15 กันยายน 2569

TL;DR: git revert เพิ่มคอมมิตใหม่ที่ยกเลิกคอมมิตเก่า — ประวัติถูกเก็บไว้ ปลอดภัยบนบรานช์ที่คนอื่น pull ไปแล้ว git reset ลากพอยเตอร์บรานช์ไปข้างหลัง — ประวัติถูกเขียนใหม่ ปลอดภัยเฉพาะคอมมิตที่ยังไม่พุช ใช้ revert บนบรานช์ร่วม ใช้ reset กับงานทำความสะอาดในเครื่อง และ reset --hard ลบการเปลี่ยนแปลงที่ยังไม่ commit ใน working tree ด้วย — นั่นคือตัวที่กินงานจริง

git revert กับ git reset ต่างกันอย่างไร?

ทั้งคู่ทำให้โปรเจกต์ดูเหมือนคอมมิตในอดีตไม่เคยเกิดขึ้น ต่างกันที่วิธี:

  • git revert <sha> คำนวณแพตช์ตรงข้ามของคอมมิตแล้ว commit ลงไป ประวัติของคุณโตขึ้นหนึ่งคอมมิตที่บอกว่า “ยกเลิกอันนั้น” คอมมิตร้ายยังอยู่ใน log ตามด้วยการยกเลิกของมัน SHA ของอย่างอื่นทั้งหมดไม่ถูกแตะ
  • git reset <sha> เลื่อนพอยเตอร์บรานช์ปัจจุบันไปที่ <sha> คอมมิตหลังจากนั้นหลุดจากสาย — ยังอยู่ในฐานอ็อบเจกต์จนกว่าจะเก็บขยะ แต่เข้าถึงไม่ได้อีกจากบรานช์ไหนหรือ git log

ตัวหนึ่งไม่เขียนทับอะไรเลย อีกตัวแสร้งว่าไม่มีอะไรเกิดขึ้น คุณสมบัติบรรทัดเดียวนี้ตัดสินว่าสถานการณ์ไหนเรียกใช้ตัวไหน:

# ตัวอย่าง: repo ทิ้งได้ที่รันได้จริง
git init /tmp/revert-vs-reset && cd /tmp/revert-vs-reset
echo one > file.txt && git add . && git commit -m "one"
echo two > file.txt && git add . && git commit -m "two"

# revert: ประวัติเก็บทั้งสองคอมมิต บวกอีกตัวที่ยกเลิก "two"
git revert --no-edit HEAD
git log --oneline        # 3 commits: revert, two, one

# reset: พอยเตอร์บรานช์ถอยหลัง "two" หายจาก log
git reset --hard HEAD~1
git log --oneline        # 1 commit: one

รันทั้งคู่แล้วส่อง git log — ความไม่สมมาตรคือคำตอบทั้งหมด

reset —soft, —mixed กับ —hard ทำอะไรกันแน่?

สามโหมดควบคุมว่าการเปลี่ยนแปลงของคุณรอดที่ไหนหลังพอยเตอร์ขยับ:

โหมดพอยเตอร์บรานช์Staging areaWorking treeการแก้ไขของคุณ
--softถอยหลังยัง stagedไม่ถูกแตะอยู่ครบ
--mixed (ดีฟอลต์)ถอยหลังยกเลิก stagedไม่ถูกแตะอยู่ แต่ยังไม่ staged
--hardถอยหลังรีเซ็ตรีเซ็ตถูกลบ

อ่านตารางนี้แบบลงมือทำได้:

  • git reset --soft HEAD~1 — “คอมมิตเร็วไป” ทุกอย่างกลับมาสถานะ staged พร้อมคอมมิตใหม่ อาจพร้อมงานเพิ่มด้วย
  • git reset HEAD~1 (mixed) — “stage ไฟล์ผิด” การแก้ไขอยู่ใน working tree ไม่มีอะไรถูก stage คู่กับการลบไฟล์ untracked เมื่ออยากให้ไฟล์เร่ร่อนหายไปด้วย
  • git reset --hard HEAD~1 — “คอมมิตนี้กับสิ่งที่ยังไม่ได้ commit ของฉันคือขยะ” git จะไม่ถามสองรอบ ไม่มี undo สำหรับส่วน working tree ถ้าไม่ได้ stash หรือ commit ไว้ก่อน

revert ไม่มีโหมดเพราะมันไม่เคยทำลายอะไรเลย — มันแค่เพิ่มคอมมิตชดเชย

เมื่อไรควรใช้ git revert แทน git reset?

ถามหนึ่งคำถาม: มีคนอื่น pull คอมมิตนี้ไปแล้วหรือยัง? ถ้าแล้ว reset ต้องออกจากโต๊ะ

การ reset บรานช์ที่คนอื่นวางรากงานไว้คือการเขียนทับประวัติร่วม pull รอบถัดไปของพวกเขาจะเจอบรานช์ที่แยกทาง และ “วิธีแก้” มักเป็น force-push ที่เปลี่ยนความผิดพลาดของคุณให้เป็นปัญหาของทุกคน revert เติมคอมมิตปกติเข้าไป ดังนั้น git pull ธรรมดาจะ merge สะอาดสำหรับทุกคน — นั่นคือเหตุผลที่ revert เป็นคำตอบดีฟอลต์บน main และบน GitHub pull request ที่ปุ่ม “Revert” มีอยู่เพราะการเขียนทับประวัติ PR ที่ merge แล้วไม่ใช่ทางเลือก

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

มีกรณีตรงกลาง: คุณพุชไปที่ feature branch ของตัวเองและไม่มีใครสร้างงานบนมัน force-push หลัง reset เป็นที่ยอมรับได้ทางสังคมที่นั่น แต่ revert ยังใช้การประสานงานน้อยกว่า เก็บ force-push ไว้กับบรานช์ที่คุณเป็นเจ้าของเพียงลำพัง

git revert ปลอดภัยกว่า git reset สำหรับบรานช์ร่วมจริงไหม?

จริงในเชิงโครงสร้าง — ไม่ใช่แค่ตามธรรมเนียม:

  • revert ผลิตคอมมิตธรรมดา: CI วิ่งบนมัน ดิฟรีวิวได้ และ git log อธิบายให้ตัวคุณในอนาคตฟังว่าทำไมการเปลี่ยนแปลงถึงหายไป
  • reset เงียบ ๆ ทิ้งสถานะระหว่างกลางไป ไม่มีใครที่รีวิวทีหลังเห็นว่าอะไรถูกยกเลิกหรือเมื่อไร เพราะ log แสดงเส้นตรงที่ไม่เคยมีมัน

สองคมที่ควรระวังในทางปฏิบัติ:

  • ย้อน merge commit ต้องใช้ git revert -m 1 <sha> (ยึดเส้นของพ่อตัวแรก) ถ้าไม่มี -m git จะปฏิเสธแล้วปล่อยให้คุณอ่าน error
  • revert ไม่ใช่เครื่องย้อนเวลา: มันยกเลิกดิฟของคอมมิตเดียว ถ้าคอมมิตทีหลังแตะบรรทัดเดิม คุณอาจต้องแก้คอนฟลิกต์ — นั่นคือ git บอกว่าการยกเลิกพันกันอยู่ ซึ่งเป็นข้อมูลที่มีประโยชน์ ไม่ใช่อาการผิดปกติ

สำหรับการย้อนคอมมิตล่าสุดของคุณโดยเฉพาะ — เก็บหรือทิ้งการเปลี่ยนแปลง — ตัวเลือกทั้งหมดวางไว้แล้วในgit undo last commit: เก็บการเปลี่ยนแปลงไว้

แล้ว git restore ล่ะ — วางอยู่ตรงไหน?

git restore (กับ git switch คู่หูของมัน) มาถึงใน git 2.23 เพื่อรับงานที่ reset ทำแย่เพราะชื่อถูกใช้มากเกิน:

งานวิธีเก่าวิธีชัดเจน
ทิ้งการแก้ working tree ของไฟล์git checkout -- file / git reset --hardgit restore file
ดึงไฟล์ออกจาก staginggit reset HEAD filegit restore --staged file
ย้ายบรานช์ไปคอมมิตอื่นgit reset <sha>git reset <sha> (ไม่มีตัวแทน — อันนี้อยู่ต่อ)

การแบ่งยุคใหม่คือ: restore ซ่อมไฟล์ reset ย้ายบรานช์ revert ยกเลิกคอมมิตที่เผยแพร่แล้ว คนค้นหา “git revert vs reset vs restore” พร้อมกันด้วยเหตุผลที่ดี — มันคือโมเดลความคิดเดียวที่กระจายอยู่ในสามคำสั่ง รูปแบบเก่าของ git checkout ยังทำงานทุกที่ รูปแบบใหม่แค่กันไม่ให้คุณส่งบรานช์ทั้งกิ่งเข้าเครื่องบด เมื่อที่คุณตั้งใจคือดึงไฟล์เดียวออกจาก staging

ถ้าเป้าหมายคือการคัดลอกคอมมิตดีไปข้างหน้า แทนการลบคอมมิตแย่ นั่นคือหน้าที่ของ cherry-pick — ดูgit cherry-pick: หลายคอมมิต หลายบรานช์ และคอนฟลิกต์

สรุปการตัดสินใจอย่างเร็ว

  1. คอมมิตสาธารณะ (พุชแล้ว คนอื่น pull แล้ว) → git revert <sha>
  2. คอมมิตอยู่ในเครื่องเท่านั้น อยากทำใหม่ → git reset --soft หรือ --mixed แล้วคอมมิตใหม่
  3. อยู่ในเครื่องเท่านั้น อยากให้หายไปพร้อมกองขยะที่ยังไม่ commit → git reset --hard หลังมองหนึ่งครั้งอย่างซื่อสัตย์ว่า working tree ยังถืออะไรอีก
  4. ไฟล์เดียวพัง → git restore <file> แล้วปล่อยบรานช์ไว้ตามเดิม

รายการเดียวในลิสต์นั้นที่อันตรายจริงคือ --hard — ที่เหลือทุกตัวคุยกลับได้ผ่าน git reflog ความสูญเสียถาวรใน git แคบมากและส่วนใหญ่ต้องให้คุณพิมพ์มันออกมาชัด ๆ นิสัยที่สมควรสร้างคือหยุดครึ่งวินาทีก่อน --hard ไม่ใช่การหลีกเลี่ยงเครื่องมือคมของ git ทั้งชุด

— mrsaynothing

— mrsaynothing

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

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

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

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

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

นี่คืออะไร?

Systemd service ไม่ยอมสตาร์ต? วิธีแก้แบบตรงจุด

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