git stash: ملف واحد دون إثارة الباقي
احفظ ملفاً واحداً في stash بينما يبقى باقي عملك الجاري مكانه: git stash push -- <مسار>، وسير -p التفاعلي، وثلاث طرق لاسترجاع تغييراتك.
يصل مراجعة كود بينما ثلاثة ملفات في منتصف التعديل، ولا يجوز أن يتحرك منها سوى واحد. حفظ الكل في stash وإعادة كتابة الملفين الآخرين — طقسٌ لا يفتقده أحد.
TL;DR: git stash push -m "السبب" -- <مسار> يحفظ المسارات المذكورة بالضبط ويترك بقية شجرتك المتسخة في سلام. استرجع التغييرات بـ git stash pop (أو اسحب ملفاً واحداً من أي stash بـ git restore --source stash@{0} -- <مسار>). صيغة pathspec وصلت مع git 2.13 الصادر في مايو 2017 — كل سلسلة أدوات خلال ثماني سنوات تملكها.
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 العاري يكنس شجرة العمل كلها — كل تعديل متتبَع يدخل الـ stash ويعود كل شيء إلى HEAD. هذه هي الصيغة الخاطئة عندما تكون الشجرة المتسخة مختلطة: ملف واحد جاهز للتسليم، والباقي ناقص بصدق. الجواب فعل push مع pathspec، وفق وثائق git-stash:
# قبل: ثلاثة ملفات متسخة
$ git status --short
M app.conf
M notes.md
M main.py
# حفظ 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 -- <مسار>وليست تزييناً: في الإصدارات الأقدم كانgit stash push -- main.pyوgit stash push main.pyيختلفان — فقد يُقرأ المسار المجرد خطأً. - الملفات غير المتتبعة تريد
-u. الملف الوليد لا يملك تاريخاً متتبعاً ليُحفظ، لذا يتجاوزهpushالعادي.git stash push -u -- newfile.pyيضمّنه؛ و-aيبعد خطوة وتكنس المتجاهَل أيضاً.
كيف أحفظ جزءاً من ملف واحد فقط؟
حين يكون الملف نفسه نصف جاهز — ثلاثة hunks جيدة، وواحد محرج — يقطعه السير التفاعلي:
git stash -p # أو: git stash push -p
يجوب Git كل hunk ويسأل Stash this hunk [y,n,q,a,d,j,g,/,e,p,?]?. أجب y للـ hunks الداخلة إلى الـ stash، وn للباقية. النتيجة: stash يحوي المقبوض فقط، وبقية الملف يبقى متسخاً في الشجرة. الآلية نفسها hunk-ب-hunk هي التي تحرّك git add -p، فتنقال ردة الفعل مباشرةً.
git stash push -- <مسار> |
git stash -p |
|
|---|---|---|
| الحبيبية | ملفات كاملة | hunks منفردة |
| السرعة | أمر واحد، قابل للسكربت | تفاعلي، hunk-ب-hunk |
| قابل للتكرار في CI | نعم (المسارات وسائط) | لا — يحتاج إنساناً عند المحث |
| الأفضل لـ | «هذا الملف، لا الباقي» | «هذا التغيير، ذاك لا» |
| منذ | git 2.13 (مايو 2017) | متاح منذ زمن |
قاعدة عملية من نموذج الوثائق نفسها: pathspec حين حدّ هو الملف، و-p حين يكون داخله.
كيف أعمل pop لملف واحد من الـstash؟
يستعيد git stash pop الملف stash@{0} ويحذف المدخلة. هذا هو الطريق الطبيعي — لكن pop مبدؤه «الكل أو لا شيء» لكل stash، ومع التعارض يتوقف مع بقاء المدخلة. خياران أدق:
# 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@{0} واقفاً. انتبه: إنها تستبدل النسخة في شجرة العمل؛ إن كانت تعديلاتك الحالية على ذلك المسار ثمينة، فقارن أولاً:
git diff stash@{0} -- app.conf
تُخزَّن الـ stash ككومتات حقيقية على كومة تشبه reflog — لذلك يقبل stash@{0} أي صياغة commit-ish، ويمكن استرداد الـ stash الذي أسقطتَه سهواً من الـ reflog حتى يأكله جامع النفايات.
متى يكون stash الأداة الخطأ؟
الـ stash ورقة مسودّة، لا فرع: لا اسم في git branch، ولا مراجعة، ولا diff يُعرض افتراضياً، ومدخلات تتراكم بصمت حتى النسيان. إذا كان عملك الجاري عليه أن ينجو من تبديلات السياق بين الآلات أو الأيام، فاعمل commit في فرع — التاريخ المرفق أفضل من كومة بلا اسم. وحين تحتاج تلك الكومتات المتوقفة إلى الهبوط في مكان آخر، ينقلها git cherry-pick. وإن كان الهدف التراجع لا الإيقاف، فتنطبق شجرة قرار التراجع عن آخر كومت.
أطوار العطل أدناه كلها من السلوك الموثّق — لا شيء مُختلَق:
| العَرَض | السبب | العلاج |
|---|---|---|
| الملف غائب بعد الـ stash | تُتجاهل المسارات غير المتتبعة | git stash push -u -- <مسار> |
error: Your local changes ... would be overwritten عند pop |
تغيّر المسار بعد الـ stash | اعمل commit أو stash للحالة الجديدة، ثم pop |
| التغييرات المستعادة اختفت | اصطدم pop بتعارض وتوقف | حُلّه، ثم git stash apply |
| «أيّ stash كان؟» | كومة stash بلا أسماء | رسالة -m في كل مرة |
ملاحظة إصدار مايو 2017 التي أضافت pathspec إلى git stash push صارت في الثامنة، ومعظم الذاكرة العضلية لـ git stash أكبر منها سناً. تستحق إعادة تعلّم: الكنس الشامل للشجرة اليوم هو الحالة الخاصة، لا الافتراضي.