เมื่อทำงานโปรเจกต์คนเดียวหรือทีม อาจมี lúc ต้องการยกเลิก commit คำสั่ง git reset เป็นเครื่องมือช่วยชีวิตจริงๆ
กลไกการติดตามของ Git (Git's Tracking Mechanism)
ก่อนไป git reset ต้องเข้าใจโครงสร้าง underlying ของ Git Git จัดการและติดตามไฟล์ผ่านโครงสร้างต้นไม้ (tree-like) มี nodes และ pointers
มี 3 "trees" หลักใน local git repository:
- Working Directory (Working tree): ไดเรกทอรี local
git statusแสดงสถานะ working directory - HEAD: snapshot commit ล่าสุดของ branch ปัจจุบัน หากสลับ branch ด้วย
git checkoutHEAD จะเปลี่ยนไป commit ล่าสุดของ branch ใหม่ - Index (Staging area): เมื่อ
git addไฟล์เพื่อ commit จะเพิ่มเข้า index นี้
Git Workflow
ตัวอย่าง: แก้ไขไฟล์ -> git add เข้า index (staging) -> git status ดู changes to be committed
เมื่อ git commit จะ save เป็น snapshot ถาวรกว่า และอัปเดต master และ HEAD ไปที่ pointer นั้น หาก git status หลัง commit จะเห็นทั้ง 3 trees ในสถานะเดียวกัน (nothing to commit)
วัตถุประสงค์ของ git reset
ทำไมต้องอธิบายมากถึง git reset? เพราะ git reset manipulate trees เหล่านี้อย่างต่างๆ รับ options ตามสิ่งที่ต้องการทำ
Git Reset Modes
สมมติ commit แล้วพบว่า commit ผิด branch หรือ commit มีบั๊ก อยาก rewind การรู้ modes ของ git reset จึงมีประโยชน์
git reset ทุก mode จะ update HEAD pointer ไวยากรณ์:
git reset <mode> <commit-optional>
Modes หลัก:
--soft: Reset HEAD pointer เหลือ index และ working directory ไม่เปลี่ยน HEAD reset แต่วันอื่นยังคง latest changes--mixed: Default Reset HEAD และ index un-stage การเปลี่ยนแปลงทั้งหมด กลับสู่ก่อนgit addNote: รันgit resetล้วนๆ จะถูกตีความเป็นgit reset --mixed--hard: ระวังใช้ นอกจาก reset HEAD, index ยัง reset working directory ด้วย อาจสูญเสียโค้ดที่เขียน! การเปลี่ยนแปลงหลัง current HEAD pointer (last commit) จะถูก discard
Modes อื่นๆ --merge และ --keep อ่านใน official documentation
เคล็ดลับ git reset ที่มีประโยชน์
Rewinding a commit (ย้อน commit)
จำว่าเว้น mode (git reset ล้วนๆ) = --mixed
พิมพ์ git reset HEAD ไม่เกิดอะไร แต่ git reset HEAD~1 HEAD จะชี้ไป commit ก่อนหน้า
ตัวอย่างต่อจากก่อนหน้า: เพิ่มข้อความในไฟล์ -> git add -> commit -> git reset HEAD~1 ทำให้ changes un-staged และอยู่ที่ commit ก่อนหน้า
วิธีเร็วและมีประโยชน์เมื่ออยาก undo commit!
Un-staging ไฟล์เฉพาะ (Un-staging a specific file)
หาก add ไฟล์เข้า index ด้วย git add นำไฟล์นั้นออกได้ด้วย:
git reset HEAD <file-name>
Scenario: ฉันทำโค้ดเสียหมด! กลับไปตอนทำงานได้ไหม?
อยากทิ้ง local changes ทั้งหมด กลับไป commit ก่อนหน้า สินค้าสุดท้ายคือ git reset --hard
บ่อยครั้งถ้าโค้ดแตก อาจเป็นทางออกเดียว หากรู้ commit hash ใช้ git reset --hard <commit> แต่ note: จะ affect commits อื่นๆ หลัง commit นั้นด้วย (ถ้ามี)!
Scenario: Commit นี้ควรอยู่ branch ใหม่!
บ่อยครั้งเมื่อเริ่มทำงาน production หากเกิดขึ้น อย่าตื่นตระหนก!
สร้าง branch ใหม่ที่มี state ของ branch ที่ต้อง rewind แล้ว reset branch ที่ได้ผลกระทบ และ checkout ไป branch ใหม่ commitต่อ там:
git branch new-branch git reset HEAD~1 --hard git checkout new-branch
Lifesaver จริงๆ!
คำเตือนสุดท้าย (One Last Word)
ระวังเมื่อ git reset --hard และ rewind ไป commit เฉพาะ โดยเฉพาะ production code และทำงานร่วมกับนักพัฒนาคนอื่น บ่อยครั้ง git revert เป็นวิธีปลอดภัยกว่าสำหรับการเปลี่ยนแปลงเหล่านี้ แต่เป็นเรื่องอีกครั้ง จนกว่าจะพบกันใหม่! 👍🟤