بازگشت به وبلاگ

git stash: یک فایل، بی‌آنکه بقیه به‌هم بریزد

۳۰ شهریور ۱۴۰۵

بازبینی کد می‌رسد در حالی که سه فایل نیمه‌کاره‌اند و فقط یکی حق دارد تکان بخورد. همه را stash کردن و دو فایل دیگر را از نو تایپ کردن — همان تشریفاتی که هیچ‌کس دلش برایش تنگ نمی‌شود.

TL;DR: git stash push -m "دلیل" -- <path> دقیقاً مسیرهای نام‌برده را stash می‌کند و بقیه درخت کثیف شما را بیکار می‌گذارد. تغییرات را با git stash pop پس بگیرید (یا یک فایل را از هر stashی جدا کنید با git restore --source stash@{0} -- <path>). حالت pathspec با git 2.13 آمد، منتشرشده در مه ۲۰۱۷ — هر زنجیره ابزار هشت سال اخیر آن را دارد.

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 کن، نه درخت را.

در git چطور فقط یک فایل را stash کنم؟

git stash لخت، کل درخت کاری را جارو می‌کند — هر تغییر ردیابی‌شده وارد stash می‌شود و همه چیز به HEAD برمی‌گردد. وقتی درخت کثیف مخلوط است این شکل غلطی است: یک فایل آماده تحویل است، بقیه صادقانه نیمه‌تمام. پاسخ فعل push با pathspec است، طبق مستندات git-stash:

# قبل: سه فایل کثیف
$ git status --short
 M app.conf
 M notes.md
 M main.py

# فقط app.conf را stash کن
$ 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 فرق داشتند؛ مسیر لخت می‌توانست بد خوانده شود.
  • فایل‌های بی‌ردیاب -u می‌خواهند. فایل تازه‌نفس هیچ تاریخ ردیابی‌شده‌ای برای stash کردن ندارد، پس push ساده از آن می‌گذرد. git stash push -u -- newfile.py آن را می‌گیرد؛ -a باز هم جلوتر می‌رود و نادیده‌گرفته‌ها را هم جارو می‌کند.

چطور فقط بخشی از یک فایل را stash کنم؟

وقتی خود فایل نیمه‌حاضر است — سه hunk خوب، یک hunk شرم‌آور — جریان تعاملی آن را ورقه می‌کند:

git stash -p            # یا: git stash push -p

Git از هر hunk می‌گذرد و می‌پرسد Stash this hunk [y,n,q,a,d,j,g,/,e,p,?]?. برای hunkهایی که به stash می‌روند y، برای آنهایی که می‌مانند n. نتیجه stashی است فقط با چیزی که تأیید کردید، و بقیه فایل کثیف در درخت می‌ماند. همان ماشین hunk‌به‌hunk، git add -p را هم می‌گرداند؛ پس رفلکس مستقیم جابه‌جا می‌شود.

git stash push -- <path>git stash -p
دانه‌بندیفایل کاملhunkهای تکی
سرعتیک فرمان، اسکریپت‌پذیرتعاملی، hunk به hunk
تکرار در CIبله (مسیرها به‌عنوان آرگومان)خیر — انسان پشت پرامپت لازم است
بهترین کاربرد«این فایل، بقیه نه»«این تغییر، آن یکی نه»
ازgit 2.13 (مه ۲۰۱۷)از مدت‌ها پیش

قاعده عملی از مدل خود مستندات: اگر مرز، خود فایل است pathspec؛ اگر داخل فایل است -p.

فایل stash‌شده را چطور پس بگیرم؟

git stash pop بازیابی می‌کند stash@{0} را و ورودی را پاک می‌کند. راه معمول — اما pop برای هر stash همه-یا-هیچ است و با تعارض، متوقف می‌شود و ورودی می‌ماند. دو گزینه ظریف‌تر:

# ۱. اعمال بدون حذف (بی‌خطر قابل تکرار)
git stash apply stash@{0}

# ۲. یک فایل را از stash بیرون بکش، ورودی سر جایش بماند
git restore --source stash@{0} -- app.conf
# املا پیش از ۲.۲۳ برای همین کار:
git checkout stash@{0} -- app.conf

شکل restore/checkout پاسخ حالت «سه تغییر را با هم stash کردم و حالا فقط یکی لازم است» است — محتوای stash‌شده آن مسیر را به درخت کاری کپی می‌کند و stash@{0} سرپا می‌ماند. توجه: نسخه درخت کاری را رونویسی می‌کند؛ اگر ویرایش‌های فعلی روی آن مسیر مهم‌اند، اول diff بگیرید:

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

stashها روی پشته‌ای شبیه reflog به‌شکل کامیت واقعی ذخیره می‌شوند — برای همین stash@{0} هر سینتکس commit-ish را می‌پذیرد، و stashی که به‌اشتباه انداخته شده تا نوبت زباله‌روبی از reflog برگردانده می‌شود.

stash کِی ابزار اشتباه است؟

stash دست‌نویس است، نه شاخه: نامی در git branch ندارد، بازبینی ندارد، diff پیش‌فرض نشان نمی‌دهد، و ورودی‌ها بی‌صدا روی هم تلنبار می‌شوند تا فراموش شوند. اگر کار در جریان باید از تعویض زمینه بین ماشین‌ها یا روزها جان به در ببرد، روی شاخه کامیتش کنید — تاریخچه همراه بهتر از تلنبار بی‌نام است. وقتی آن کامیت‌های پارک‌شده باید بعداً جای دیگری فرود بیایند، git cherry-pick حمل‌ونقل می‌کند. و اگر هدف پس‌گرفتن است نه پارک کردن، درخت تصمیمِ خنثی‌سازی آخرین کامیت حاکم است.

دفتر صادق خرابی‌ها، همه از رفتار مستندشده:

نشانهعلتدرمان
فایل بعد از stash غایب استمسیرهای بی‌ردیاب رد می‌شوندgit stash push -u -- <path>
در pop خطای error: Your local changes ... would be overwrittenمسیر بعد از stash عوض شدهوضعیت جدید را کامیت یا stash کن، بعد pop
تغییرهای برگشته گم شدندpop با تعارض متوقف شدحل کن، بعد git stash apply
«کدام stash بود؟»تلنبار stash بی‌نامهر بار پیام -m

یادداشت انتشار مه ۲۰۱۷ که pathspec را به git stash push افزود حالا هشت‌ساله است و بیشتر حافظه عضلانی git stash از آن هم پیرتر. یک بار یادگیری دوباره می‌ارزد: امروز جاروی کل درخت حالت خاص است، نه پیش‌فرض.

FAQ

در git چطور فقط یک فایل را stash کنم؟

git stash push -m "یادداشت" -- path/to/file — حالت pathspec فقط مسیرهای نام‌برده را stash می‌کند و بقیه فایل‌های کثیف را دست‌نخورده می‌گذارد. git 2.13 یا جدیدتر لازم است (مه ۲۰۱۷).

چطور یک فایل را از یک stash جدا کنم؟

git restore --source stash@{0} -- path/to/file (یا شکل قدیمی‌تر git checkout stash@{0} -- path/to/file). نسخه stash‌شده را به درخت کاری شما کپی می‌کند بدون آنکه ورودی stash را پاک کند.

چرا git stash فایل جدید من را رد شد؟

فایل‌های بی‌ردیاب (untracked) بخشی از stash معمولی نیستند. -u اضافه کنید: git stash push -u -- path/to/file. فایل‌های نادیده‌گرفته‌شده -a می‌خواهند.

— mrsaynothing

— mrsaynothing

یادداشت‌های میدانی درباره AI، لینوکس و self-hosting.

آموزش بعدی با ایمیل

هر نوشته یک ایمیل. درستش کن، برو سراغ بعدی.

self-hosted · بدون واسطه‌های ثالث · لغو اشتراک با یک کلیک

این چیست؟

یادداشت میدانی از سایت agent-run، #1: ماشین می‌فرستد، من تأیید می‌کنم

این نوشته‌ها را می‌پسندید؟ این همان کاری است که برای زندگی از آن درمی‌آورم. استخدامم کنید