เวรแล้ว ไอ้ Git!?!

Git ยาก ฉิบหายได้ง่าย และการจะหาวิธีแก้ไขข้อผิดพลาดที่เกิดขึ้นนั้น เป็นไปไม่ได้เลย เอกสารของ Git เป็นปัญหาไก่กับไข่ คุณไม่มีทางหาวิธีเอาตัวรอดจากความยุ่งเหยิงได้ เว้นเสียแต่ว่า คุณรู้วิธีแก้ปัญหาอยู่แล้ว

นี่คือสถานการณ์แย่ ๆ ที่ฉันเคยพาตัวเองเข้าไป และวิธีการที่ฉันเอาตัวรอดออกมาได้ โดยอธิบายด้วยภาษาง่าย ๆ

เวรแล้ว! กูทำพัง! บอกทีว่า git มีเวทมนตร์ย้อนเวลาได้ !?!

git reflog
# คุณจะเห็นรายการที่คุณทำไปบน git
# ทุกรายการ ทุก branch
# แต่ละรายการมี index HEAD@{index}
# มองหา index ก่อนหน้าที่คุณทำมันพัง
git reset HEAD@{index}
# เวทมนตร์ย้อนเวลา

คุณสามารถใช้คำสั่งนี้ในการเรียกคืนสิ่งที่คุณพลาดลบทิ้งไป หรือล้างสิ่งที่ทำให้ repo พัง หรือใช้กู้ repo จากการ merge พลาด หรือ กลับไปเวลาที่ของยังใช้ได้ปกติ ฉันใช้ reflog บ่อยมาก ต้องขอบคุณหลาย ๆ คนที่แนะนำให้เพิ่มสิ่งนี้เข้ามา

เวรแล้ว! กูสั่ง commit ไปแล้วเพิ่งนึกขึ้นได้ว่าต้องแก้อะไรเพิ่มอีกนิดหน่อย!

# แก้ไขตามต้องการ
git add . # หรือ add ไฟล์ที่ต้องการ
git commit --amend --no-edit
# ทีนี้ commit สุดท้ายก็จะรวมสิ่งที่แก้ไขไปด้วย
# คำเตือน: อย่าแก้ไข commits สาธารณะ

สถานการณ์นี้เกิดขึ้นประจำเวลาที่ฉัน commit แล้วรัน tests/linters... แล้วก็พบว่า เฮ้ย ลืมเติมช่องว่างหลังเครื่องหมายเท่ากับ คุณสามารถทำให้การเปลี่ยนแปลงเป็น commit ใหม่ แล้วทำ rebase -i เพื่อจับมัดรวมกันก็ได้ แต่วิธีนี้เร็วกว่าเป็นล้านเท่า

คำเตือน: อย่าแก้ commits ที่ push เข้า public/shared branch เป็นอันขาด ให้แก้เฉพาะ commits บน local copy ในเครื่องเท่านั้น ไม่งั้นคุณลำบากแน่

เวรแล้ว! กูอยากแก้ข้อความของ commit สุดท้าย!

git commit --amend
# แก้ไขข้อความของ commit สุดท้าย เมื่อ prompt ปรากฏ

ลืมจัดรูปแบบข้อความของ commit ตามเงื่อนไขอีกแล้ว

เวรแล้ว! กูเผลอ commit เข้า master แทนที่จะทำ branch ใหม่!

# สร้าง branch ใหม่จากสถานะปัจจุบันของ master
git branch some-new-branch-name
# ลบ commit สุดท้ายออกจาก master
git reset HEAD~ --hard
git checkout some-new-branch-name
# commit ย้ายไปอยู่ใน branch ใหม่แล้ว :)

หมายเหตุ: วิธีนี้ใช้งานไม่ได้ ถ้าคุณ push commit เข้า public/shared branch ไปแล้ว และถ้าคุณได้ทำอย่างอื่นไปก่อนหน้านี้ คุณอาจต้องสั่ง git reset HEAD@{number-of-commits-back} แทนการสั่ง HEAD~ และเช่นเคย มีหลายคนแนะนำวิธีเจ๋ง ๆ นี่โดยที่ฉันไม่เคยรู้มาก่อน ช่วยให้ขั้นตอนในการทำงานสั้นกว่าเดิม ขอบคุณทุกคนมาก!

เวรแล้ว! กูเผลอ commit ผิด branch!

# ยกเลิก commit สุดท้าย แต่คงการเปลี่ยนแปลงไว้
git reset HEAD~ --soft
git stash
# เปลี่ยนไป branch ที่ถูกต้อง
git checkout name-of-the-correct-branch
git stash pop
git add . # หรือ add ไฟล์ที่ต้องการ
git commit -m "your message here"
# ทีนี้ การเปลี่ยนแปลงอยู่บน branch ที่ถูกต้องแล้ว

หลายคนแนะนำว่าใช้ cherry-pick ในสถานการณ์นี้ก็ได้ เลือกเอาก็แล้วกันว่าแบบไหน makes sense สำหรับคุณมากกว่ากัน!

git checkout name-of-the-correct-branch
# เลือกเอา commit สุดท้ายจาก master
git cherry-pick master
# แล้วลบมันออกจาก master
git checkout master
git reset HEAD~ --hard

เวรแล้ว! กูสั่ง diff ไป แต่ไม่มีอะไรขึ้นมาเลย?!

ถ้าคุณรู้แน่ ๆ ว่าคุณเปลี่ยนแปลงไฟล์ แต่สั่ง diff แล้วได้ความว่างเปล่า คุณอาจจะได้ add-ed ไฟล์เข้า staging และคุณต้องใส่ flags พิเศษเพิ่มเข้าไป

git diff --staged

¯\_(ツ)_/¯ (ใช่ ฉันรู้ว่ามันคือฟีเจอร์ ไม่ใช่บัก แต่นี่เป็นสิ่งที่ไม่มีทางเข้าใจได้ง่ายเลยเวลาที่เกิดขึ้นกับคุณครั้งแรก!)

เวรแล้ว! กูอยากย้อนกลับมาแถว ๆ 5 commits ก่อนหน้านี้!

# หา commit ที่ต้องการยกเลิก
git log
# ใช้แป้นลูกศรเลื่อนดูในประวัติ
# พอหา commit ที่คุณต้องการเจอ จดค่า hash ไว้
git revert [saved hash]
# git จะสร้าง commit ใหม่ที่ใช้ยกเลิก commit นั้น
# แก้ไขข้อความของ commit ตามต้องการ
# หรือไม่ก็บันทึกและ commit

กลายเป็นว่า ถ้าจะยกเลิกการเปลี่ยนแปลง คุณไม่จำเป็นต้องย้อนกลับไปดูแล้ว copy-paste ข้อมูลในไฟล์ใหม่แล้ว! ถ้าคุณเผลอ commit บักเข้าไป คุณสามารถยกเลิก commit ที่มีบักได้เลยด้วย revert.

คุณยังสามารถ revert แค่ไฟล์เดียวแทนที่จะทั้ง commit ก็ได้นะ! แต่แน่นอน ตามวิถีของ git จะทำแบบนั้น ต้องใช้คำสั่งคนละชุดกัน...

เวรแล้ว! กูอยากยกเลิกการแก้ไขเฉพาะไฟล์นี้ไฟล์เดียว!

# หา hash ของ commit ก่อนหน้าที่ไฟล์จะถูกแก้ไข
git log
# ใช้แป้นลูกศรเลื่อนดูในประวัติ
# พอหา commit ที่คุณต้องการเจอ จดค่า hash ไว้
git checkout [saved hash] -- path/to/file
# ไฟล์เวอร์ชันเดิมนั้นจะมาอยู่ที่ index ที่คุณอยู่
git commit -m "Wow, you don't have to copy-paste to undo"

ตอนที่เราหาวิธีนี้เจอ เป็นการค้นพบที่ยิ่งใหญ่มาก ใหญ่สุด ๆ เอาจริง ๆ นะ มีดาวดวงไหนที่คำสั่ง checkout -- มัน make sense ในการ undo ไฟล์บ้าง? :ชู-กำปั้น-ใส่-ลินุส-ทอร์วาลด์:

ช่างแม่ง! กูยอมแพ้แล้ว!

cd ..
sudo rm -r stupid-git-repo-dir
git clone https://some.github.url/stupid-git-repo-dir.git
cd stupid-git-repo-dir

ขอบคุณ Eric V. สำหรับคำแนะนำ ถ้าจะโวยว่าทำไมต้องใช้ sudo ในมุกนี้ โวยตรงไปที่ Eric V. ได้เลย

ตอบแบบจริงจัง ถ้า branch ของคุณมันพัง จนต้องรีเซ็ตสถานะกลับไปให้เหมือนกับ remote repo โดยใช้วิธีที่เป็นที่ยอมรับใน"วิถีของ git" แล้วละก็ ลองทำตามนี้ แต่ขอเตือนว่ามันคือคำสั่งทำลายล้างทุกอย่างและจะไม่สามารถกู้อะไรกลับมาได้!

# ดึงสถานะล่าสุดจาก origin
git fetch origin
git checkout master
git reset --hard origin/master
# ลบไฟล์และไดเรกทอรีที่ไม่เกี่ยวทิ้ง
git clean -d --force
# สั่ง checkout/reset/clean บน branch ที่พังทีละ branch

*คำปฏิเสธความรับผิดชอบ: เว็บไซต์นี้ไม่ได้ตั้งใจสร้างมาเพื่อใช้เป็นแหล่งอ้างอิง และใช่ ยังมีวิธีอื่นที่สามารถทำสิ่งเดียวกันนี้ได้ด้วยวิธีที่สละสลวยกว่า หรืออะไรก็ตาม แต่ฉันได้วิธีการเหล่านี้จากการลองผิดลองถูก สบถด่าและคว่ำโต๊ะหลายต่อหลายครั้ง ฉันเลยได้ไอเดียที่จะแบ่งปันสิ่งเหล่านี้ จะเอาหรือไม่เอาก็แล้วแต่คุณ!

ขอบคุณทุกคนที่อาสาแปลเว็บไซต์นี้เป็นภาษาต่าง ๆ พวกคุณเจ๋งมาก! Björn Söderqvist (sv) · Moritz Stückler (de) · Daniil Golubev (ru) · Łukasz Wójcik (pl) · fedemcmac (it) · Michel (fr) · Andriy Sultanov (ua) · Meiko Hori (ja) · Alex Tzimas (gr) · Martijn ten Heuvel (nl) · Elad Leev (he) · Franco Fantini (es) · Catalina Focsa (ro) · Davi Alexandre (pt_BR) · Nemanja Vasić (sr) · Tao Jiayuan (zh) · Eduard Tomek (cs) · Ricky Gultom (id) · Khaja Md Sher E Alam (bn) · Rahul Dahal (ne) · Taha Paksu (tr) · Kitt Tientanopajai (th) · Gyeongjae Choi (ko) ด้วยความช่วยเหลือเพิ่มเติมจาก Iain Murray · Frank Taillandier · David Fyffe · Lucas Larson · Artem Vorotnikov

หากคุณอยากจะช่วยแปลเป็นภาษาของคุณเพิ่มเติม ส่ง PR มาได้ที่ GitHub