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

git stash: ไฟล์เดียว โดยไม่รบกวนที่เหลือ

21 กันยายน 2569

Code review มาถึงระหว่างที่สามไฟล์แก้ครึ่ง ๆ กลาง ๆ และมีไฟล์เดียวที่ขยับได้ การ stash ทุกอย่างแล้วพิมพ์ไฟล์อีกสองใหม่คือพิธีกรรมที่ไม่มีใครคิดถึง

TL;DR: git stash push -m "เหตุผล" -- <path> จะ stash เฉพาะพาธที่ระบุและปล่อยส่วนที่เหลือของ tree ที่ยังไม่ได้คอมมิตให้เป็นอิสระ ดึงการเปลี่ยนแปลงกลับด้วย git stash pop (หรือดึงไฟล์เดียวจาก stash ไหนก็ได้ด้วย git restore --source stash@{0} -- <path>) รูปแบบ pathspec มาพร้อม git 2.13 ที่ปล่อยเมื่อพฤษภาคม 2017 — ทุก toolchain ในแปดปีหลังมีมัน

git stash push -m "app.conf prod tweak" -- app.conf
git stash list
# stash@{0}: On main: app.conf prod tweak
git stash pop

stash ไฟล์ ไม่ใช่ทั้งต้นไม้

จะ stash เฉพาะไฟล์เดียวใน git ได้อย่างไร

git stash เปล่า ๆ จะกวาดทั้ง working tree — การแก้ไขทุกอย่างที่ถูกติดตามพากันเข้า stash และทุกอย่างกลับไปที่ HEAD ซึ่งเป็นรูปทรงที่ผิดเมื่อ tree ที่ยังไม่คอมมิต ปนกัน: ไฟล์หนึ่งพร้อมส่งต่อ ส่วนที่เหลือตรงไปตรงมาคือยังไม่เสร็จ คำตอบคือกริยา push พร้อม pathspec ตามเอกสาร git-stash:

# ก่อนหน้า: สามไฟล์ที่ยังไม่คอมมิต
$ git status --short
 M app.conf
 M notes.md
 M main.py

# stash เฉพาะ app.conf
$ git stash push -m "app.conf prod tweak" -- app.conf
Saved working directory and index state On main: app.conf prod tweak

$ git status --short
 M notes.md
 M main.py

app.conf กลับสู่สถานะที่คอมมิตไว้และไปอยู่ใน stash@{0}; notes.md กับ main.py ไม่ขยับแม้นิด ข้อความ -m เป็นทางเลือก แต่คุ้มสามวินาทีทันทีที่ git stash list เริ่มมีมากกว่าหนึ่งรายการ — stash ที่ตั้งชื่อว่า “WIP” แก่เร็ว

สองรายละเอียดที่สำคัญ:

  • pathspec อยู่หลัง -- ทุกอย่างหลังขีดคู่คือพาธ ไม่ใช่แฟล็ก เป็นธรรมเนียมเดียวกับ git checkout -- <path> และไม่ใช่ของประดับ: ในเวอร์ชันเก่า git stash push -- main.py กับ git stash push main.py ต่างกันได้ เมื่อพาธเปล่าถูกอ่านผิด
  • ไฟล์ untracked ต้องการ -u ไฟล์เกิดใหม่ไม่มีประวัติที่ถูกติดตามให้ stash ดังนั้น push ปกติจะข้ามมัน git stash push -u -- newfile.py จะรวมเข้ามา; -a ไปไกลกว่านั้นและกวาดไฟล์ที่ถูก ignore ด้วย

จะ stash เฉพาะบางส่วนของไฟล์เดียวได้อย่างไร

เมื่อไฟล์นั้นเองยังครึ่งเสร็จ — สาม hunk ที่โอเค หนึ่ง hunk ที่อาย — โหมดโต้ตอบจะซอยมัน:

git stash -p            # หรือ: git stash push -p

Git เดินผ่านทีละ hunk และถาม Stash this hunk [y,n,q,a,d,j,g,/,e,p,?]? ตอบ y สำหรับ hunk ที่เข้า stash, n สำหรับที่อยู่ต่อ ผลลัพธ์คือ stash ที่มีเฉพาะส่วนที่อนุมัติ ส่วนที่เหลือของไฟล์ยังอยู่ใน tree กลไกทีละ hunk ชุดเดียวกันนี้ยังขับเคลื่อน git add -p ดังนั้นสัญชาตญาณย้ายบ้านได้ทันที

git stash push -- <path>git stash -p
ความละเอียดทั้งไฟล์hunk ราย hunk
ความเร็วคำสั่งเดียว เขียนสคริปต์ได้โต้ตอบ ทีละ hunk
ทำซ้ำใน CIได้ (พาธเป็นอาร์กิวเมนต์)ไม่ได้ — ต้องมีคนยืนที่ prompt
เหมาะที่สุดกับ“ไฟล์นี้ ที่เหลือไม่เอา”“การเปลี่ยนนี้ อันนั้นไม่เอา”
ตั้งแต่git 2.13 (พ.ค. 2017)มีมานานแล้ว

กฎละมือจากโมเดลของเอกสารเอง: pathspec เมื่อเส้นแบ่งคือไฟล์, -p เมื่อมันอยู่ข้างใน

จะดึงไฟล์ที่ stash ไว้กลับมาได้อย่างไร

git stash pop คืน stash@{0} และลบรายการ นั่นคือเส้นทางปกติ — แต่ pop เป็นทุกอย่างหรือไม่มีอะไรเลยต่อหนึ่ง stash และเมื่อชน conflict จะหยุดกลางคันโดยรายการยังอยู่ สองทางเลือกที่ละเอียดกว่า:

# 1. ใช้งานโดยไม่ลบ (ทำซ้ำได้ปลอดภัย)
git stash apply stash@{0}

# 2. ดึงไฟล์เดียวออกจาก stash โดยรายการยังยืนอยู่
git restore --source stash@{0} -- app.conf
# รูปเขียนก่อน 2.23 ของการทำงานเดียวกัน:
git checkout stash@{0} -- app.conf

รูปแบบ restore/checkout ตอบกรณี “stash รวมสามการเปลี่ยนไว้ด้วยกัน ตอนนี้ต้องการแค่อันเดียว” — มันคัดลอกเนื้อหาใน stash ของพาธนั้นเข้า working tree และปล่อยให้ stash@{0} ยังยืน ข้อควรระวัง: มันเขียนทับสำเนาใน working tree; ถ้าการแก้ปัจจุบันบนพาธนั้นสำคัญ ขอ diff ก่อน:

git diff stash@{0} -- app.conf

stash ถูกเก็บเป็นคอมมิตจริงบนกองที่คล้าย reflog — นั่นคือเหตุที่ stash@{0} รับวากยสัมพันธ์ commit-ish ได้ทุกแบบ และ stash ที่โดนทิ้งผิดพลาดยังกู้จาก reflog ได้จนกว่า garbage collection จะกินมัน

stash เป็นเครื่องมือที่ผิดเมื่อไหร่

stash คือกระดาษทด ไม่ใช่ branch: ไม่มีชื่อใน git branch, ไม่มี review, ไม่แสดง diff ตามค่าเริ่มต้น และรายการกองสะสมอย่างเงียบ ๆ จนลืม ถ้างานที่กำลังทำต้องรอดจากการเปลี่ยนบริบทข้ามเครื่องหรือข้ามวัน คอมมิตลง branch เถอะ — ประวัติที่แนบมาดีกว่ากองไร้นาม เมื่อคอมมิตที่จอดไว้ต้องลงจอดที่อื่น git cherry-pick ขนส่งให้ และถ้าเป้าหมายคือย้อน ไม่ใช่จอด ให้ใช้ต้นไม้การตัดสินใจจากย้อนคอมมิตล่าสุด

สมุดบัญชีความพังที่ซื่อสัตย์ ทุกกรณีมาจากพฤติกรรมที่เอกสารระบุ:

อาการสาเหตุวิธีแก้
ไฟล์หายไปหลัง stashพาธ untracked ถูกข้ามgit stash push -u -- <path>
error: Your local changes ... would be overwritten ตอน popพาธเปลี่ยนไปหลังจาก stashคอมมิตหรือ stash สถานะใหม่ แล้ว pop
การเปลี่ยนที่ดึงมาหายวับpop เจอ conflict แล้วหยุดแก้ conflict แล้ว git stash apply
“stash อันไหนนะ?”กอง stash ไร้นามใส่ข้อความ -m ทุกครั้ง

หน้า release note พฤษภาคม 2017 ที่เติม pathspec ให้ git stash push อายุแปดปีแล้ว และความจำกล้ามเนื้อส่วนใหญ่ของ git stash แก่กว่านั้น น่าเรียนใหม่สักรอบ: การกวาดทั้งต้นทุกวันนี้คือกรณีพิเศษ ไม่ใช่ค่าเริ่มต้น

FAQ

จะ stash เฉพาะไฟล์เดียวใน git ได้อย่างไร

git stash push -m "โน้ต" -- path/to/file — รูปแบบ pathspec จะ stash เฉพาะพาธที่ระบุและปล่อยไฟล์ที่แก้ไขไฟล์อื่นไว้ตามเดิม ต้องใช้ git 2.13 ขึ้นไป (พฤษภาคม 2017)

จะดึงไฟล์เดียวออกจาก stash ได้อย่างไร

git restore --source stash@{0} -- path/to/file (หรือรูปแบบเก่า git checkout stash@{0} -- path/to/file) คัดลอกเวอร์ชันใน stash ไปยัง working tree ของคุณโดยไม่ลบรายการ stash

ทำไม git stash ถึงข้ามไฟล์ใหม่ของฉันไป

ไฟล์ที่ยังไม่ถูกติดตาม (untracked) ไม่ได้อยู่ใน stash ปกติ ใส่ -u เพื่อรวมเข้ามา: git stash push -u -- path/to/file ส่วนไฟล์ที่ถูก ignore ต้องใช้ -a

— mrsaynothing

— mrsaynothing

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

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

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

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

นี่คืออะไร?

บันทึกหน้างาน #1 เว็บที่เอเจนต์ดูแล: เครื่อง deploy คนอนุมัติ

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