คู่มือ

git stash: พักการเปลี่ยนแปลงชั่วคราวและกู้คืนได้อย่างไร

อัปเดต: · Skillaria.Top

git stash นำการเปลี่ยนแปลงที่ยังไม่ได้คอมมิตไปเก็บไว้ในที่พัก และคืนไดเรกทอรีทำงานให้ตรงกับคอมมิตล่าสุด เพื่อให้คุณสลับไปทำงานอื่นได้ กู้คืนด้วย git stash pop (นำกลับมาและลบรายการออกจากที่พัก) หรือ git stash apply (นำกลับมาและเก็บรายการไว้) ไม่มีคำสั่ง git unstash แยกต่างหาก

ทำไมต้องใช้ git stash

สถานการณ์ที่พบบ่อยคือคุณกำลังทำงานค้างอยู่ ไฟล์ถูกแก้ไขแล้ว แต่ยังไม่ถึงเวลาคอมมิต แล้วก็มีคนขอให้แก้บั๊กด่วนในอีกบรานช์หนึ่ง การสลับบรานช์ทำไม่ได้ หรือกลัวว่าการแก้ไขจะหาย git stash จะเก็บการเปลี่ยนแปลงที่ยังไม่คอมมิตทั้งหมดไว้ "ในกระเป๋า" และคืนไดเรกทอรีทำงานให้ตรงกับคอมมิตล่าสุด

$ git stash
Saved working directory and index state WIP on feature/login: 3f2a1c4 ฟอร์มเข้าสู่ระบบ

$ git status
On branch feature/login
nothing to commit, working tree clean

จากนั้นคุณสามารถสลับบรานช์ แก้บั๊ก กลับมา และดึงสิ่งที่พักไว้ออกมาได้ ทุกครั้งที่เซฟจะเป็นหนึ่งรายการในสแตก stash ภายใน Git เก็บรายการนั้นเป็นคอมมิตพิเศษ ข้อมูลจึงปลอดภัยและไม่ผูกกับบรานช์

วิธีเก็บการเปลี่ยนแปลง: ตัวเลือกหลัก

ใส่คำอธิบายที่เข้าใจได้

คำอธิบายมาตรฐาน "WIP on ชื่อบรานช์" จะไม่บอกอะไรเลยหลังผ่านไปหนึ่งสัปดาห์ ควรตั้งชื่อกำกับไว้:

git stash push -m "ตรวจความถูกต้องของฟอร์ม ยังไม่ทำอีเมล"

git stash ที่ไม่มีอาร์กิวเมนต์เป็นตัวย่อของ git stash push รูปแบบเก่า git stash save "ข้อความ" ยังใช้ได้ แต่ในสถานการณ์ใหม่ให้ใช้ push -m

รวมไฟล์ใหม่ด้วย

ค่าเริ่มต้นของ stash จะเก็บเฉพาะไฟล์ที่ถูกติดตามอยู่แล้ว ได้แก่ไฟล์ที่แก้ไขหรือเพิ่มเข้าดัชนีแล้ว ไฟล์ใหม่ที่ยังไม่เคยผ่าน git add จะยังคงอยู่ในไดเรกทอรีทำงาน ซึ่งมักทำให้ประหลาดใจ

git stash -u        # --include-untracked: รวมไฟล์ที่ยังไม่ถูกติดตามด้วย
git stash -a        # --all: รวมไฟล์จาก .gitignore ด้วย

ควรระวังกับ -a เพราะ node_modules ไฟล์บิลด์ และคอนฟิกส่วนตัวจะถูกเก็บไปด้วย โดยทั่วไป -u ก็เพียงพอแล้ว

เก็บเฉพาะบางส่วนของการเปลี่ยนแปลง

git stash push -m "คอนฟิก" src/config.js   # เฉพาะไฟล์ที่ระบุ
git stash -p                                # เลือกส่วนของโค้ดแบบโต้ตอบ
git stash --staged                          # เฉพาะสิ่งที่อยู่ในดัชนี (Git 2.35+)
git stash --keep-index                      # พักทั้งหมด แต่คงดัชนีไว้ในโฟลเดอร์

--keep-index มีประโยชน์ก่อนคอมมิต คุณจะเหลือในไดเรกทอรีทำงานเฉพาะสิ่งที่จะคอมมิต รันเทสต์ และตรวจว่าคอมมิตนั้นสมบูรณ์ในตัวเอง

วิธีกู้คืนการเปลี่ยนแปลง (git unstash)

ใน Git ไม่มีคำสั่ง git unstash คนส่วนใหญ่มักค้นหาคำนี้เมื่อต้องการดึงสิ่งที่พักไว้ออกมา ซึ่งทำได้ด้วยสองคำสั่งหลัก

คำสั่งทำอะไรรายการในสแตก
git stash popนำการเปลี่ยนแปลงที่พักล่าสุดกลับมาใช้ถูกลบ หากนำกลับมาได้โดยไม่มีความขัดแย้ง
git stash applyนำการเปลี่ยนแปลงที่พักล่าสุดกลับมาใช้ยังคงอยู่
git stash pop stash@{2}นำรายการที่ระบุกลับมาใช้ถูกลบ
git stash apply --indexกู้คืนทั้งการแยกระหว่างดัชนีและไดเรกทอรีทำงานยังคงอยู่
$ git stash pop
On branch feature/login
Changes not staged for commit:
        modified:   src/auth.js
        modified:   src/form.js
Dropped refs/stash@{0} (8c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d)

โปรดสังเกตว่าโดยค่าเริ่มต้น แม้สิ่งที่อยู่ในดัชนีตอนพักจะถูกกู้คืนเป็นการเปลี่ยนแปลงธรรมดา หากต้องการคงสถานะดัชนีที่เตรียมไว้ ให้เพิ่ม --index

pop หรือ apply

สำหรับการทำงานทุกวัน pop สะดวกกว่า: ดึงออกมาแล้วจบ ส่วน apply เหมาะเมื่อต้องการนำการเปลี่ยนแปลงเดียวกันไปใช้ในหลายบรานช์ หรือไม่มั่นใจว่าจะกู้คืนได้ราบรื่น รายการจะยังอยู่เป็นตาข่ายนิรภัย แล้วค่อยลบภายหลังด้วย git stash drop

การจัดการรายการที่พักไว้

$ git stash list
stash@{0}: On feature/login: ตรวจความถูกต้องของฟอร์ม ยังไม่ทำอีเมล
stash@{1}: WIP on main: 9b7e0d2 อัปเดต dependencies
stash@{2}: On feature/api: ทดลองใช้แคช

สแตกทำงานแบบ "เข้าหลังออกก่อน" คือ stash@{0} คือรายการใหม่ล่าสุดเสมอ และหมายเลขจะเลื่อนทุกครั้งที่มีการ push หรือ drop

คำสั่งหน้าที่
git stash showสถิติการเปลี่ยนแปลงแบบย่อของรายการล่าสุด
git stash show -p stash@{1}diff เต็มของรายการที่ระบุ
git stash drop stash@{1}ลบหนึ่งรายการ
git stash clearลบทุกรายการพร้อมกัน
git stash branch ชื่อ stash@{0}สร้างบรานช์จากคอมมิตที่ใช้พัก นำรายการกลับมาใช้ และลบรายการนั้น

หากต้องการให้ git stash show แสดง diff เต็มแทนสถิติ และรวมไฟล์ที่ยังไม่ถูกติดตามด้วย ตั้งค่า stash.showPatch และ stash.showIncludeUntracked ได้

ขั้นตอนตัวอย่าง: บั๊กด่วนระหว่างทำงาน

  1. พักงานปัจจุบันพร้อมไฟล์ใหม่: git stash push -u -m "ล็อกอิน: ฉบับร่าง"
  2. สลับไปบรานช์หลักและสร้างบรานช์สำหรับแก้ไข: git switch main, git pull, git switch -c hotfix/null-email
  3. แก้ข้อผิดพลาด คอมมิต และส่งเพื่อรีวิว
  4. กลับไปยังงานเดิม: git switch feature/login
  5. นำการเปลี่ยนแปลงกลับมา: git stash pop ตรวจด้วย git status ว่าทุกอย่างอยู่ครบ

ปัจจุบันหลายคนใช้ git worktree ในสถานการณ์นี้ คือมีอีกโฟลเดอร์ทำงานของรีโพซิทอรีเดียวกัน ทำให้ไม่ต้องพักอะไรเลย แต่สำหรับการสลับชั่วคราวไม่กี่นาที stash ยังง่ายกว่า

ความขัดแย้งเมื่อนำ stash กลับมาใช้

หากบรานช์เปลี่ยนแปลงบรรทัดเดียวกันหลังจากพักไว้ การนำกลับมาจะจบด้วยความขัดแย้ง:

$ git stash pop
Auto-merging src/auth.js
CONFLICT (content): Merge conflict in src/auth.js
The stash entry is kept in case you need it again.

ในกรณีนี้รายการจะไม่ถูกลบ แม้จะใช้ pop ก็ตาม แก้ความขัดแย้งในไฟล์ตามปกติ จากนั้นรัน git add เพื่อทำเครื่องหมายว่าแก้แล้ว (หรือ git restore --staged หากไม่ต้องการให้อยู่ในดัชนี) แล้วลบรายการที่ไม่ใช้แล้วด้วย git stash drop

หากความขัดแย้งซับซ้อน มีทางเลือกที่ง่ายกว่า: git stash branch tmp-login Git จะสร้างบรานช์จากคอมมิตเดิมที่คุณพักงานไว้ การเปลี่ยนแปลงจะนำกลับมาได้โดยไม่มีความขัดแย้ง และจากนั้นคุณค่อยรวมกับบรานช์หลักด้วย merge ปกติ

วิธีกู้คืน stash ที่ลบไปโดยไม่ตั้งใจ

หากคุณรัน git stash drop หรือ clear โดยผิดพลาด ยังไม่ใช่ว่าจะสูญหายทั้งหมด รายการนั้นเป็นคอมมิต และตราบใดที่ garbage collector ยังไม่ลบมัน คุณสามารถหามันเจอในหมู่อ็อบเจกต์ที่ "ลอยอยู่":

git fsck --no-reflog | grep "dangling commit"
git show 8c1d2e3        # ตรวจว่าเป็น stash ที่ต้องการหรือไม่
git stash apply 8c1d2e3

หากเพิ่งลบรายการด้วย drop หรือ pop แฮชจะถูกพิมพ์ออกมาในผลลัพธ์ของคำสั่ง ในบรรทัด Dropped refs/stash@{0} (...) ส่งค่านั้นให้ git stash apply ได้เลย

โครงสร้างภายในของ stash

การเข้าใจโครงสร้างจะช่วยลดความกลัวการสูญเสียข้อมูล รายการแต่ละรายการของ stash คือคอมมิตธรรมดาที่ถูกชี้ด้วยการอ้างอิง refs/stash มันมีพาเรนต์สองตัว: คอมมิต HEAD ที่คุณอยู่ตอนนั้น และคอมมิตแยกที่เก็บสถานะของดัชนี เมื่อใช้ -u จะมีพาเรนต์ตัวที่สามสำหรับไฟล์ที่ไม่ถูกติดตาม รายการ stash@{N} ก็คือ reflog ของการอ้างอิง refs/stash

จากนี้มีผลในทางปฏิบัติหลายข้อ คุณส่ง stash ให้คำสั่งใดก็ได้ที่รับคอมมิต เช่น git diff stash@{0} หรือ git show stash@{0}:src/auth.js (เนื้อหาของไฟล์เดียวในรายการ) หรือ git checkout stash@{0} -- src/auth.js เพื่อดึงไฟล์เดียวโดยไม่ต้องนำทั้งรายการกลับมา และรายการที่ลบไปจะยังอยู่ในฐานข้อมูลอ็อบเจกต์จนกว่า garbage collection จะลบมัน

Stash กับคำสั่ง Git อื่น

บางคำสั่งสามารถพักการเปลี่ยนแปลงให้ได้เอง git rebase --autostash และ git pull --rebase --autostash จะพักการแก้ไขที่ยังไม่คอมมิตก่อนเริ่ม และนำกลับมาหลังจบ หากต้องการให้พฤติกรรมนี้เป็นค่าถาวร ตั้งค่า rebase.autoStash true และ merge.autoStash true หากการนำกลับมาเกิดความขัดแย้ง Git จะเก็บรายการไว้ในสแตก stash ตามปกติและแจ้งให้ทราบ

การสลับบรานช์ด้วย git switch ก็ไม่จำเป็นต้องใช้ stash เสมอไป หากการเปลี่ยนแปลงของคุณไม่ทับกับไฟล์ที่ต่างกันระหว่างบรานช์ Git จะพาการเปลี่ยนแปลงนั้นไปด้วย stash จำเป็นเมื่อ Git ตอบว่า "Your local changes would be overwritten by checkout" หรือเมื่อคุณไม่ต้องการพาการแก้ไขไปยังบรานช์อื่น

ข้อผิดพลาดที่พบบ่อย

  • ไฟล์ใหม่ "ไม่ถูกพัก". ไฟล์เหล่านั้นไม่ได้ถูกติดตาม จึงต้องใช้ -u
  • ใช้ stash เป็นที่เก็บระยะยาว. ผ่านไปหนึ่งเดือนจะมี "WIP on main" ไม่มีชื่อสิบรายการ และไม่มีใครจำได้ว่าอยู่ในนั้นคืออะไร สำหรับงานระยะยาว ให้คอมมิตในบรานช์แยก อย่างน้อยก็ push ขึ้นไปได้
  • ลืมว่ารายการยังอยู่หลังเกิดความขัดแย้ง แล้วนำกลับมาใช้อีกครั้งภายหลัง หลังแก้ความขัดแย้งเสร็จ ให้ทำ drop เสมอ
  • stash ไม่ซิงก์กับเซิร์ฟเวอร์. มันเก็บอยู่ในรีโพซิทอรีในเครื่องของคุณเท่านั้น git push จะไม่ส่งมันขึ้นไป หากลบโฟลเดอร์โปรเจกต์ stash จะหายไป
  • นำรายการผิดกลับมาใช้. ก่อน pop ให้ดู git stash list และ git stash show -p

สรุป

git stash เป็นวิธีที่รวดเร็วในการเคลียร์ไดเรกทอรีทำงานโดยไม่สูญเสียการแก้ไขที่ยังไม่คอมมิต ใช้ git stash push -u -m "คำอธิบาย" เพื่อพัก และ git stash pop เพื่อกู้คืน ใช้ apply เมื่อต้องการตาข่ายนิรภัย ใช้ git stash list และ show -p เพื่อไม่ให้สับสน และอย่าเปลี่ยน stash ให้เป็นที่เก็บของ สำหรับงานทุกอย่างที่ต้องทำต่อเนื่องยาวนาน มีคอมมิตและบรานช์ไว้ใช้ การสลับระหว่างงานด้วย stash จะฝึกได้จริงในหลักสูตร Git

คำถามที่พบบ่อย

สามารถนำ stash ไปใช้ในบรานช์อื่นได้หรือไม่?

ได้ เพราะ stash ไม่ผูกกับบรานช์ สลับไปยังบรานช์ที่ต้องการแล้วรัน git stash pop หรือ apply หากการเปลี่ยนแปลงทับซ้อนกัน จะเกิดความขัดแย้งตามปกติ

git stash เก็บการเปลี่ยนแปลงในดัชนีไว้หรือไม่?

เก็บ แต่เมื่อใช้ pop หรือ apply ตามปกติ ทุกอย่างจะกลับมาเป็นการเปลี่ยนแปลงที่ยังไม่ถูกเตรียม หากต้องการกู้คืนดัชนีด้วย ให้ใช้ git stash apply --index

สามารถเก็บรายการใน stash ได้กี่รายการ?

ไม่มีขีดจำกัดตายตัว แต่รายการที่เก่ากว่าอายุการเก็บของ reflog อาจถูกลบโดย garbage collection ในทางปฏิบัติควรเก็บไว้ในสแตกไม่เกินสองสามรายการล่าสุด

ทำ stash เฉพาะหนึ่งไฟล์ได้อย่างไร?

ระบุพาธหลัง -- หรือต่อท้ายคำสั่ง push: git stash push -m คำอธิบาย -- src/config.js จะมีเพียงไฟล์นี้ที่ถูกพัก ส่วนการแก้ไขอื่นยังคงอยู่ในไดเรกทอรีทำงาน

stash ต่างจากคอมมิตชั่วคราวอย่างไร?

คอมมิตอยู่ในบรานช์ ส่งขึ้นเซิร์ฟเวอร์ได้ และแก้ภายหลังด้วย amend หรือ rebase ได้ ส่วน stash เก็บไว้เฉพาะในเครื่องในสแตกแยก และเหมาะกับการสลับงานระยะสั้น

เรียนหัวข้อนี้ผ่านวิดีโอ

หัวข้อนี้สอนอย่างละเอียดพร้อมฝึกปฏิบัติในคอร์ส “Git с нуля до уверенного разработчика”

ไปที่คอร์ส