เข้าใจ git reset: เลือก --soft, --mixed และ --hard อย่างปลอดภัย
ทำความเข้าใจว่า git reset เปลี่ยน commit history, staging area และ working tree อย่างไร พร้อมแนวทางเลือกใช้ --soft, --mixed และ --hard ให้เหมาะสมและปลอดภัยต่อการทำงานเป็นทีม

git reset เป็นคำสั่งสำหรับย้ายตำแหน่งของ branch ปัจจุบันไปยัง commit ที่กำหนด พร้อมเปลี่ยนสถานะของ staging area และ working tree ตามโหมดที่เลือก คำสั่งนี้ช่วยจัดระเบียบ commit ในเครื่องของเราได้ดี แต่บางโหมดอาจทำให้การแก้ไขที่ยังไม่ได้ commit สูญหาย จึงควรเข้าใจผลของแต่ละโหมดก่อนใช้งาน
git reset ทำงานกับอะไรบ้าง
ความสับสนของ git reset มักเกิดจากคำสั่งเดียวสามารถกระทบพื้นที่สำคัญของ Git ได้พร้อมกันถึงสามส่วน เมื่อแยกแต่ละส่วนออกจากกัน เราจะมองเห็นได้ชัดเจนขึ้นว่าโหมดใดเก็บงานไว้ และโหมดใดนำงานออก
- Commit history คือประวัติ commit ที่ branch ปัจจุบันชี้อยู่ การ reset ไปยัง commit ก่อนหน้าจะย้าย pointer ของ branch กลับไปยังตำแหน่งนั้น
- Staging area คือพื้นที่เก็บการเปลี่ยนแปลงที่เลือกไว้สำหรับ commit ครั้งถัดไป ไฟล์ที่ผ่าน
git addจะอยู่ในพื้นที่นี้ - Working tree คือไฟล์ที่กำลังแก้ไขอยู่ในโฟลเดอร์งาน สถานะของไฟล์อาจแตกต่างจากทั้ง commit ล่าสุดและ staging area
โหมด --soft, --mixed และ --hard ต่างกันตรงพื้นที่ที่ถูกเปลี่ยนหลังจากย้าย branch pointer แล้ว หากจำผลต่อสามพื้นที่นี้ได้ การเลือกใช้ git reset จะง่ายและปลอดภัยขึ้นมาก
เปรียบเทียบโหมดของ git reset
| โหมด | ย้าย branch pointer | เก็บไฟล์ใน staging | เก็บการแก้ไขใน working tree |
|---|---|---|---|
--soft | ใช่ | ใช่ | ใช่ |
--mixed | ใช่ | ไม่ | ใช่ |
--hard | ใช่ | ไม่ | ไม่เก็บการแก้ไขของไฟล์ที่ Git ติดตาม |
ย้อน commit แต่เก็บงานไว้ใน staging ด้วย --soft
คำสั่งต่อไปนี้จะยกเลิก commit ล่าสุดในประวัติของ branch ปัจจุบัน แต่ไม่เปลี่ยน staging area และ working tree การแก้ไขจาก commit ที่ถูกย้อนจึงยังพร้อมสำหรับสร้าง commit ใหม่ทันที
git reset --soft HEAD~1
โหมดนี้เหมาะเมื่อเพิ่ง commit แล้วพบว่าต้องแก้ข้อความ commit เพิ่มไฟล์ที่ลืมใส่ หรือรวมหลาย commit ให้เป็น commit เดียว หลังจากปรับงานเรียบร้อยแล้วสามารถ commit ใหม่ได้โดยไม่ต้องเลือกไฟล์ทั้งหมดเข้าสู่ staging อีกครั้ง
ย้อน commit และนำไฟล์ออกจาก staging ด้วย --mixed
--mixed จะย้าย branch pointer และปรับ staging area ให้ตรงกับ commit เป้าหมาย แต่ยังเก็บการแก้ไขไว้ใน working tree ไฟล์จึงไม่หาย เพียงกลับมาอยู่ในสถานะที่ยังไม่ได้เลือกสำหรับ commit
git reset --mixed HEAD~1
โหมดนี้เป็นค่าเริ่มต้นของ git reset ดังนั้นคำสั่งด้านล่างให้ผลแบบเดียวกัน เหมาะเมื่ออยากย้อน commit แล้วจัดกลุ่มไฟล์ด้วย git add ใหม่ก่อนสร้าง commit ที่มีขอบเขตชัดเจนกว่าเดิม
git reset HEAD~1
ย้อน commit และทิ้งการแก้ไขด้วย --hard
--hard จะทำให้ branch pointer, staging area และ working tree ตรงกับ commit เป้าหมาย การแก้ไขที่ยังไม่ได้ commit ในไฟล์ซึ่ง Git ติดตามอยู่อาจถูกทิ้ง จึงเป็นโหมดที่ต้องตรวจสอบสถานะให้รอบคอบที่สุด
git reset --hard HEAD~1
ไม่ควรใช้โหมดนี้เพียงเพราะต้องการให้สถานะ Git ดูสะอาด หากยังไม่แน่ใจว่ามีงานใดต้องเก็บไว้ ให้เริ่มจาก --soft หรือ --mixed และตรวจสอบผลก่อนเสมอ
วิธีเลือกโหมดให้ตรงกับสิ่งที่ต้องการ
ก่อนใช้ git reset ให้ตอบคำถามสองข้อ ได้แก่ ต้องการย้อน branch ไปยัง commit ใด และต้องการเก็บการแก้ไขปัจจุบันไว้ที่ใด จากนั้นจึงเลือกโหมดตามผลลัพธ์ที่ต้องการ
- ต้องการย้อน commit แต่ให้ไฟล์พร้อม commit ใหม่ทันที ให้ใช้
--soft - ต้องการย้อน commit เก็บไฟล์ไว้ และเลือก staging ใหม่ด้วยตนเอง ให้ใช้
--mixed - ต้องการให้ทั้ง staging area และไฟล์ที่ Git ติดตามกลับไปตรงกับ commit เป้าหมาย โดยยอมทิ้งการแก้ไขปัจจุบัน ให้ใช้
--hard
ควรตรวจสอบสถานะและประวัติ commit ก่อนดำเนินการทุกครั้ง คำสั่งเหล่านี้ช่วยให้เห็นทั้งไฟล์ที่กำลังแก้ ตำแหน่ง branch และความแตกต่างที่อยู่ใน staging area
git status
git log --oneline --decorate -n 10
git diff
git diff --staged
หากยังลังเลว่าจะเก็บไฟล์หรือไม่ ให้ถือว่าต้องเก็บไว้ก่อน เพราะการนำไฟล์ออกจาก staging สามารถแก้ไขภายหลังได้ง่ายกว่าการกู้การเปลี่ยนแปลงที่ถูก --hard ทิ้งไปแล้ว
reset กับ revert ในการทำงานเป็นทีม
git reset เหมาะกับการปรับประวัติ commit ที่ยังอยู่ใน branch local และยังไม่มีผู้อื่นนำไปใช้งาน หาก commit ถูก push ไปยัง branch ที่สมาชิกทีมอาจ pull แล้ว การ reset และ push ประวัติชุดใหม่อาจทำให้ branch ของแต่ละคนไม่ตรงกันและเกิด conflict ที่ไม่จำเป็น
ในกรณีที่ commit ถูกเผยแพร่แล้ว ควรใช้ git revert เพื่อสร้าง commit ใหม่ที่ย้อนผลของ commit เดิม วิธีนี้รักษาประวัติร่วมของทีมไว้และไม่เปลี่ยน commit ที่ผู้อื่นอาจมีอยู่ในเครื่องแล้ว
git revert <commit-hash>
กฎที่นำไปใช้ได้ง่ายคือ ใช้ reset เพื่อแก้ประวัติส่วนตัวที่ยังไม่เผยแพร่ และใช้ revert เพื่อย้อนการเปลี่ยนแปลงในประวัติที่แชร์กับผู้อื่นแล้ว ก่อน rewrite history ควรแน่ใจเสมอว่าไม่มีสมาชิกทีมพึ่งพา commit ชุดนั้นอยู่
สรุป
หัวใจของการใช้ git reset คือการเข้าใจความสัมพันธ์ระหว่าง commit history, staging area และ working tree โดย --soft เก็บงานไว้ใน staging, --mixed นำงานออกจาก staging แต่ยังเก็บไฟล์ และ --hard ทำให้ทั้ง staging กับไฟล์ที่ติดตามกลับไปตรงกับ commit เป้าหมาย
ก่อนใช้คำสั่งควรตรวจสอบ git status และ git log --oneline พร้อมตัดสินใจให้ชัดว่าต้องการเก็บการแก้ไขไว้หรือไม่ สำหรับ branch local สามารถใช้ reset เพื่อจัดประวัติได้ แต่เมื่อ commit ถูกแชร์กับทีมแล้ว การใช้ revert มักเป็นทางเลือกที่ปลอดภัยกว่า