رفتن به محتوا
LoopX

stash

توی این درس یاد می‌گیری وسط کار، وقتی فوراً باید روی شاخه‌ی دیگری کاری بکنی و نمی‌خواهی commit نیمه‌کاره بزنی، تغییرهایت را موقتاً کنار بگذاری: git stash پوشه‌ی کاری را تمیز می‌کند و تغییرها را در یک «کشو» نگه می‌دارد، git stash list کشوها را می‌شمارد، و git stash pop (یا apply) برشان می‌گرداند. می‌فهمی چطور stash را نام‌گذاری کنی، فایل‌های untracked را هم بگیری (-u)، فقط بخشی از تغییرها را کنار بگذاری (-p)، و از stash یک شاخه بسازی. تمرین: وسط کار، یک باگ فوری روی شاخه‌ی دیگر را درست کن و برگرد.

مسئله: وسط کار، یک کار فوری

Section titled “مسئله: وسط کار، یک کار فوری”

تو داری روی یک ویژگی کار می‌کنی، پنج فایل نیمه‌کاره است و هیچ‌کدام درست کار نمی‌کند. همین‌جا مدیر می‌گوید «یک باگ فوری روی main داریم». Git اجازه نمی‌دهد با تغییر ثبت‌نشده به شاخه‌ی دیگر بروی (اگر فایل‌ها تداخل کنند)، و یک commit نیمه‌کاره‌ی wip هم تاریخچه را کثیف می‌کند. git stash راه سوم است: همه‌ی تغییرهای ثبت‌نشده را در یک کشوی موقت می‌گذارد و پوشه‌ات را به حالت آخرین commit برمی‌گرداند. کار فوری را می‌کنی، برمی‌گردی و کشو را خالی می‌کنی.

وسط نوشتن یک نامه‌ی طولانی، تلفن زنگ می‌زند و باید فوراً یک فرم را پر کنی. نامه‌ی نیمه‌کاره را توی کشو می‌گذاری (میز تمیز می‌شود)، فرم را پر می‌کنی و بعد نامه را از کشو درمی‌آوری و ادامه می‌دهی. می‌توانی چند کار نیمه‌کاره را روی هم در کشو بگذاری؛ آخرین چیزی که گذاشتی اول بیرون می‌آید (مثل یک پشته).

stash پوشه‌ی کاری و staging را در یک پشته ذخیره می‌کند و پوشه را به حالت آخرین commit برمی‌گرداند. pop آخرین stash را برمی‌گرداند و از پشته برمی‌دارد؛ apply برمی‌گرداند ولی نگه می‌دارد.

یک پروژه‌ی کوچک با دو commit؛ و یک شاخه‌ی ویژگی که وسطش هستیم:

Terminal window
mkdir -p ~/gitlab/app && cd ~/gitlab/app && git init -q
printf '# Wellcome to LoopApp\n' > README.md
printf 'def home():\n return "home"\n' > app.py
git add . && git commit -qm "Add app skeleton"
git switch -qc feature/profile
printf '\ndef profile(user):\n return f"profile of {user}"\n' >> app.py
git commit -qam "Add profile function"
git log --oneline --all
خروجی
4258b67 Add profile function
0b7fbb4 Add app skeleton

وسط کار تغییرهایی داری (یکی در فایل دنبال‌شده، یکی staged):

Terminal window
cd ~/gitlab/app
printf '\ndef avatar(user):\n return "TODO"\n' >> app.py
echo "profile notes" > NOTES.md && git add NOTES.md
echo "--- وضعیت وسط کار:"
git status -s
echo "--- git stash:"
git stash
echo "--- پوشه تمیز شد:"
git status -s; echo "(خالی)"
echo "--- کشو:"
git stash list
echo "--- git stash pop (برگرداندن):"
git stash pop
git status -s
خروجی
--- وضعیت وسط کار:
A NOTES.md
M app.py
--- git stash:
Saved working directory and index state WIP on feature/profile: 4258b67 Add profile function
--- پوشه تمیز شد:
(خالی)
--- کشو:
stash@{0}: WIP on feature/profile: 4258b67 Add profile function
--- git stash pop (برگرداندن):
On branch feature/profile
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: NOTES.md
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: app.py
Dropped refs/stash@{0} (1e2a3ebab7d083cba1057dbb141226fb72063792)
A NOTES.md
M app.py

git stash همه‌ی تغییرهای دنبال‌شده (modified و staged) را در کشو می‌گذارد و پوشه‌ی کاری را به آخرین commit برمی‌گرداند. هر stash یک نام دارد: stash@{0} تازه‌ترین است. git stash pop تازه‌ترین را برمی‌گرداند و از پشته برمی‌دارد. (دقت کن فایل تازه‌ی NOTES.md که add شده بود، به‌صورت staged (A ) برگشت؛ ولی درباره‌ی فایل‌های موجود، pop معمولی حالت staged را برنمی‌گرداند. مثال ۴ نشانش می‌دهد.)

مثال ۲: سناریوی واقعی، باگ فوری

Section titled “مثال ۲: سناریوی واقعی، باگ فوری”

وسط کار هستی؛ غلط املایی Wellcome در README باید فوراً درست شود:

Terminal window
cd ~/gitlab/app
git stash -q
echo "=== به main می‌روم و باگ را درست می‌کنم:"
git switch -q main
sed -i 's/Wellcome/Welcome/' README.md
git commit -qam "Fix typo in README"
git log --oneline -2
echo "=== برمی‌گردم به شاخه‌ی خودم و کارم را از کشو می‌آورم:"
git switch -q feature/profile
git stash pop -q
git status -s
git branch --show-current
خروجی
=== به main می‌روم و باگ را درست می‌کنم:
de7cb4c Fix typo in README
0b7fbb4 Add app skeleton
=== برمی‌گردم به شاخه‌ی خودم و کارم را از کشو می‌آورم:
A NOTES.md
M app.py
feature/profile

سه دستور: stash، کار فوری، stash pop. (اگر باگ فوری روی همین شاخه نبود و می‌خواستی وسط کار شاخه‌ی دیگری بسازی، همین الگوست.) حالا feature/profile تغییرهای نیمه‌کاره‌اش را دوباره دارد و main باگ را درست‌شده دارد.

وقتی چند کار نیمه‌کاره داری، پیام‌های پیش‌فرض (WIP on feature/profile: abc...) همه شبیه‌اند و گیج می‌شوی. با -m اسم می‌دهی:

Terminal window
cd ~/gitlab/app
git stash -q -m "avatar function half done"
printf '# Extra notes\n' >> README.md
git stash push -q -m "readme notes"
echo "--- فهرست کشو (تازه‌ترین بالاتر):"
git stash list
echo "--- محتوای stash قدیمی‌تر (خلاصه‌ی فایل‌ها):"
git stash show 'stash@{1}'
echo "--- و خود تغییر (-p):"
git stash show -p 'stash@{1}'
خروجی
--- فهرست کشو (تازه‌ترین بالاتر):
stash@{0}: On feature/profile: readme notes
stash@{1}: On feature/profile: avatar function half done
--- محتوای stash قدیمی‌تر (خلاصه‌ی فایل‌ها):
NOTES.md | 1 +
app.py | 3 +++
2 files changed, 4 insertions(+)
--- و خود تغییر (-p):
diff --git a/NOTES.md b/NOTES.md
new file mode 100644
index 0000000..addacb0
--- /dev/null
+++ b/NOTES.md
@@ -0,0 +1 @@
+profile notes
diff --git a/app.py b/app.py
index 7ec22a2..7c12e00 100644
--- a/app.py
+++ b/app.py
@@ -3,3 +3,6 @@ def home():
def profile(user):
return f"profile of {user}"
+
+def avatar(user):
+ return "TODO"

git stash push -m "پیام" (همان git stash save قدیمی) اسم می‌گذارد. git stash list فهرست را می‌دهد و git stash show stash@{N} یک stash را بدون برگرداندنش نشان می‌دهد (با -p تغییر کامل). برای مشخص‌کردن یکی، stash@{N} را بنویس (داخل '...' تا شل {} را تفسیر نکند).

مثال ۴: pop در برابر apply، و پاک‌کردن

Section titled “مثال ۴: pop در برابر apply، و پاک‌کردن”

pop = برگرداندن + برداشتن از پشته. apply = فقط برگرداندن، stash در کشو می‌ماند (مثلاً برای اعمال همان تغییر روی چند شاخه):

Terminal window
cd ~/gitlab/app
echo "--- apply یک stash مشخص (stash@{1}):"
git stash apply -q 'stash@{1}'
git status -s
echo "--- کشو هنوز هر دو را دارد:"
git stash list
git restore app.py
echo "--- drop یکی، clear همه:"
git stash drop 'stash@{0}'
git stash list
git stash clear
git stash list; echo "(خالی)"
خروجی
--- apply یک stash مشخص (stash@{1}):
A NOTES.md
M app.py
--- کشو هنوز هر دو را دارد:
stash@{0}: On feature/profile: readme notes
stash@{1}: On feature/profile: avatar function half done
--- drop یکی، clear همه:
Dropped stash@{0} (17421ffbbfe0cb55f6f0b4776cdad411c3780a36)
stash@{0}: On feature/profile: avatar function half done
(خالی)
دستور کار
git stash pop تازه‌ترین را برگردان و از پشته بردار
git stash apply [stash@{N}] برگردان ولی در پشته نگه دار
git stash drop [stash@{N}] یکی را بدون برگرداندن پاک کن
git stash clear کل پشته را پاک کن ⚠
git stash pop --index تغییرهای staged را هم دوباره staged کن

تفاوت --index را همین‌جا ببین: فایل موجود را ویرایش و add می‌کنم (staged)، stash می‌کنم و دو بار برمی‌گردانم:

Terminal window
mkdir -p ~/gitlab/idx && cd ~/gitlab/idx && git init -q
echo "v1" > f.txt && git add f.txt && git commit -qm "Add f"
echo "v2" > f.txt && git add f.txt
git stash -q
echo "--- pop معمولی (staged به unstaged تبدیل می‌شود):"
git stash pop -q && git status -s
git add f.txt && git stash -q
echo "--- pop --index (حالت staged حفظ می‌شود):"
git stash pop --index -q && git status -s
خروجی
--- pop معمولی (staged به unstaged تبدیل می‌شود):
M f.txt
--- pop --index (حالت staged حفظ می‌شود):
M f.txt

مثال ۵: فایل‌های جدید، -u

Section titled “مثال ۵: فایل‌های جدید، -u”

به‌صورت پیش‌فرض stash فقط فایل‌های دنبال‌شده را می‌گیرد. فایل جدید (untracked) سر جایش می‌ماند:

Terminal window
cd ~/gitlab/app
git restore . 2>/dev/null
echo "جدید" > draft.txt
printf '\n# edit\n' >> app.py
echo "--- با git stash (بدون -u):"
git stash
ls draft.txt && echo "(draft.txt هنوز این‌جاست؛ stash نشد)"
git stash pop -q
echo "--- با git stash -u (untracked را هم می‌گیرد):"
git stash -u
ls draft.txt 2>&1 | head -1
git stash pop -q
ls draft.txt
خروجی
--- با git stash (بدون -u):
Saved working directory and index state WIP on feature/profile: 4258b67 Add profile function
draft.txt
(draft.txt هنوز این‌جاست؛ stash نشد)
--- با git stash -u (untracked را هم می‌گیرد):
Saved working directory and index state WIP on feature/profile: 4258b67 Add profile function
ls: cannot access 'draft.txt': No such file or directory
draft.txt
گزینه چه چیزی را هم می‌گیرد
(پیش‌فرض) فایل‌های دنبال‌شده‌ی ویرایش‌شده و staged
-u (--include-untracked) + فایل‌های جدید (untracked)
-a (--all) + حتی فایل‌های ignore‌شده (مراقب باش؛ مثلاً node_modules)
-k (--keep-index) آنچه staged است در پوشه می‌ماند

اگر -u نزنی و فکر کنی «همه‌چیز» کنار رفته، فایل‌های جدید ممکن است با چیزی که می‌خواهی تست کنی قاطی شوند.

مثال ۶: stash بخشی از تغییرها، -p و مسیر

Section titled “مثال ۶: stash بخشی از تغییرها، -p و مسیر”

گاهی فقط بعضی تغییرها را می‌خواهی کنار بگذاری. دو راه: مسیر فایل و -p (patch، تکه‌تکه):

Terminal window
cd ~/gitlab/app
git restore .
git clean -fdq
echo "ex" >> README.md
printf '\n# ex2\n' >> app.py
echo "--- دو فایل عوض شده:"
git status -s
git stash push -q -m "only README" -- README.md
echo "--- فقط README در کشو رفت؛ app.py سر جایش:"
git status -s
git stash list | head -1
git stash drop -q
git restore app.py
خروجی
--- دو فایل عوض شده:
A NOTES.md
M README.md
M app.py
--- فقط README در کشو رفت؛ app.py سر جایش:
A NOTES.md
M app.py
stash@{0}: On feature/profile: only README

git stash push -- مسیر فقط فایل‌های مشخص‌شده را کنار می‌گذارد. و git stash push -p برای هر تکه‌ی تغییر (hunk) می‌پرسد (y بگیر، n نگیر) مثل git add -p. (در اسکریپت جواب‌ها را با printf 'y\nn\n' | git stash push -p می‌دهی.)

مثال ۷: از stash یک شاخه بساز، git stash branch

Section titled “مثال ۷: از stash یک شاخه بساز، git stash branch”

اگر pop مشکل‌ساز است (مثلاً شاخه‌ی قدیمی شده و تداخل دارد) یا وسط کار تصمیم گرفته‌ای این تغییر شاخه‌ی خودش را بگیرد، از stash یک شاخه بساز: شاخه از همان commit ای ساخته می‌شود که stash روی آن گرفته شد، پس conflict نمی‌گیری:

Terminal window
cd ~/gitlab/app
printf '\ndef settings():\n return "settings"\n' >> app.py
git stash -q -m "settings idea"
git switch -q main
echo "main moved" >> README.md && git commit -qam "Main moves on"
echo "--- از stash یک شاخه‌ی تازه می‌سازم:"
git stash branch feature/settings 'stash@{0}'
git status -s
git branch --show-current
git stash list | wc -l
git commit -qam "Add settings function"
git log --oneline --graph main feature/settings
خروجی
--- از stash یک شاخه‌ی تازه می‌سازم:
Switched to a new branch 'feature/settings'
On branch feature/settings
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: NOTES.md
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: app.py
Dropped stash@{0} (a1334cf4c22ed33baceb05dc10460336a9c70ae5)
A NOTES.md
M app.py
feature/settings
0
* 0e3ea31 Main moves on
* de7cb4c Fix typo in README
| * d281af9 Add settings function
| * 4258b67 Add profile function
|/
* 0b7fbb4 Add app skeleton

git stash branch نام شاخه را از commit ای که stash در آن گرفته شد می‌سازد، به آن می‌رود، pop می‌کند و stash را پاک می‌کند: همه‌ی کار در یک دستور.

اگر در این فاصله همان خط‌ها در شاخه عوض شده باشند، pop تداخل می‌دهد. نکته‌ی مهم: وقتی pop conflict دهد، stash پاک نمی‌شود (تا مطمئن شوی):

Terminal window
mkdir -p ~/gitlab/stashc && cd ~/gitlab/stashc && git init -q
echo "version: 1" > cfg && git add cfg && git commit -qm "Base"
echo "version: 2 (my work)" > cfg
git stash -q
echo "version: 3 (someone else)" > cfg && git commit -qam "Others change"
echo "=== pop:"
git stash pop 2>&1 | head -4
echo "=== وضعیت و کشو:"
git status -s
git stash list
echo "=== حل: نسخه‌ی نهایی، add و drop دستی:"
echo "version: 3 (merged)" > cfg
git add cfg && git restore --staged cfg
git stash drop
git stash list; echo "(کشو خالی)"
خروجی
=== pop:
Auto-merging cfg
CONFLICT (content): Merge conflict in cfg
On branch main
Unmerged paths:
=== وضعیت و کشو:
UU cfg
stash@{0}: WIP on main: 7dac84f Base
=== حل: نسخه‌ی نهایی، add و drop دستی:
Dropped refs/stash@{0} (b4a5e00933d96a4fc82da2e6b1ceb6402cf821de)
(کشو خالی)

بعد از حل conflict، چون pop خودش stash را پاک نکرده بود، باید git stash drop بزنی. (و git add فقط برای علامت‌زدن حل‌شدن بود؛ با restore --staged از staging درش آوردم تا مثل قبل فقط یک تغییر ثبت‌نشده بماند.)

مثال ۹: برگرداندن stash پاک‌شده

Section titled “مثال ۹: برگرداندن stash پاک‌شده”

drop و pop هش stash را در پیام چاپ می‌کنند؛ اگر اشتباهاً پاکش کردی، تا وقتی garbage collection اجرا نشده، با آن هش برش می‌گردانی:

Terminal window
mkdir -p ~/gitlab/lost && cd ~/gitlab/lost && git init -q
echo a > f && git add f && git commit -qm "Base"
echo "کار مهم" > f
git stash -q
out=$(git stash drop)
echo "$out"
hash=$(echo "$out" | sed -E 's/.*\(([0-9a-f]+)\).*/\1/')
echo "--- stash پاک شد؛ کشو خالی:"
git stash list; echo "(خالی)"
echo "--- برگرداندن با هش:"
git stash apply $hash
cat f
خروجی
Dropped refs/stash@{0} (2b075676674d72228946c1dd2735ce03a2c73e03)
--- stash پاک شد؛ کشو خالی:
(خالی)
--- برگرداندن با هش:
On branch main
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: f
no changes added to commit (use "git add" and/or "git commit -a")
کار مهم

Dropped refs/stash@{0} (هش): هشی که چاپ شد را نگه دار. اگر هش را ندیدی: git fsck --no-reflog | grep 'dangling commit' commit های بی‌صاحب (از جمله stash های پاک‌شده) را فهرست می‌کند.

stash معجزه نیست؛ Git در واقع یک commit خاص می‌سازد: tree آن عکس پوشه‌ی کاری توست، یک والدش commit فعلی (HEAD) و والد دومش یک commit برای staging. برچسب refs/stash به آخرین stash اشاره می‌کند و stash های قدیمی‌تر در reflog همین برچسب‌اند (برای همین پشته است). ببین:

Terminal window
mkdir -p ~/gitlab/inside && cd ~/gitlab/inside && git init -q
echo 1 > f && git add f && git commit -qm "Base"
echo 2 > f && echo n > n && git add n
git stash -q -u
echo 3 > f && git stash -q
echo "--- stash ها همان reflog ِ refs/stash‌اند:"
git reflog show stash
echo "--- ساختار یک stash (commit با چند والد):"
git cat-file -p 'stash@{1}' | grep -E '^(tree|parent)'
echo "--- و پیام:"
git log -1 --format=%s 'stash@{1}'
خروجی
--- stash ها همان reflog ِ refs/stash‌اند:
07f05bf stash@{0}: WIP on main: 9a66b23 Base
dfefc55 stash@{1}: WIP on main: 9a66b23 Base
--- ساختار یک stash (commit با چند والد):
tree c9b61a85f0e21a9b145d5932be2e27bcf26ceee0
parent 9a66b239c06bb8f1fee86bf300afeeb0f26904c3
parent b464b76ce717b30032bdf2e06702330df175387b
parent 016c1b3821cfc29c07403226e678a950e975439e
--- و پیام:
WIP on main: 9a66b23 Base

والدهای stash (parent ها): اولی HEAD زمان stash، دومی commit حالت index، و (با -u) سومی commit فایل‌های untracked. به همین دلیل stash مثل هر commit دیگر در .git/objects می‌ماند، ولی هرگز با push به سرور نمی‌رود و روی کامپیوتر دیگر هم نیست؛ پس stash یک حافظه‌ی محلی و موقت است.

دستور کار
git stash / git stash push کنار گذاشتن تغییرهای دنبال‌شده
git stash push -m "پیام" با نام
git stash -u با فایل‌های untracked
git stash push -- مسیر فقط فایل‌های مشخص
git stash push -p تکه‌تکه انتخاب کن
git stash list فهرست
git stash show [-p] [stash@{N}] محتوای یک stash
git stash pop برگردان و بردار
git stash apply [stash@{N}] برگردان و نگه دار
git stash drop [stash@{N}] / git stash clear حذف یکی / همه
git stash branch نام [stash@{N}] شاخه از stash
روش چه زمانی
git stash کنار گذاشتن خیلی کوتاه‌مدت (دقیقه تا ساعت)، برگشتن به همان شاخه
commit موقت wip روی شاخه‌ی شخصی کار چندروزه یا می‌خواهی روی چند کامپیوتر داشته باشی (stash push نمی‌شود)
git stash branch کار نیمه‌کاره که می‌خواهی شاخه‌ی خودش را بگیرد
Terminal window
mkdir -p ~/gitlab/forgot && cd ~/gitlab/forgot && git init -q
echo a > f && git add f && git commit -qm "Base"
echo "no changes yet"
git stash 2>&1
for i in 1 2 3; do echo "$i" >> f; git stash -q -m "idea $i"; done
git stash list
خروجی
no changes yet
No local changes to save
stash@{0}: On main: idea 3
stash@{1}: On main: idea 2
stash@{2}: On main: idea 1

اول: بدون تغییر، git stash خطا نمی‌دهد ولی چیزی هم نمی‌سازد (No local changes to save). و دوم: stash ها بی‌سروصدا جمع می‌شوند و بعد از ماه‌ها نمی‌دانی stash@{7} چه بود. راه‌حل: همیشه با -m اسم بگذار، هر چند وقت git stash list را نگاه کن و قدیمی‌ها را drop کن.

۲) فایل‌های جدید با stash نرفتند

Section titled “۲) فایل‌های جدید با stash نرفتند”

مثال ۵: git stash بدون -u فایل‌های جدید را نمی‌برد. راه‌حل: git stash -u. و قبل از stash یک بار git status بخوان که چه چیزی را نگرفته است.

stash به شاخه وصل نیست؛ می‌توانی آن را روی هر شاخه‌ای pop کنی. اگر روی شاخه‌ی اشتباه pop کردی، تغییرها همان‌جا می‌آیند. راه‌حل: قبل از pop git branch --show-current؛ و اگر لازم بود git stash branch.

۴) تکیه‌ی بلندمدت به stash

Section titled “۴) تکیه‌ی بلندمدت به stash”

stash روی سرور نمی‌رود و بکاپ ندارد؛ اگر git stash clear بزنی یا پوشه را پاک کنی، رفت. راه‌حل: کار مهم را روی یک شاخه‌ی شخصی commit کن (حتی نیمه‌کاره) و push کن.

۵) pop با conflict و پاک‌شدن نامرئی

Section titled “۵) pop با conflict و پاک‌شدن نامرئی”

مثال ۸: بعد از conflict، stash هنوز هست (امن). فراموش نکن بعد از حل drop کنی.

✎ تمرینآسان

فایلی را ویرایش کن، git stash بزن و ثابت کن پوشه تمیز است؛ بعد git stash pop بزن و ثابت کن ویرایش برگشت.

دیدن جواب
Terminal window
mkdir -p ~/gitlab/ex1 && cd ~/gitlab/ex1 && git init -q
echo "v1" > a.txt && git add a.txt && git commit -qm "Add a"
echo "v2 (editing)" > a.txt
git stash
git status -s; echo "(پوشه تمیز)"; cat a.txt
git stash pop
cat a.txt
خروجی
Saved working directory and index state WIP on main: 0ce8426 Add a
(پوشه تمیز)
v1
On branch main
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: a.txt
no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{0} (97c56f463deac61ac327763d67ca77901957238b)
v2 (editing)
✎ تمرینمتوسط

تمرین اصلی: وسط کار، یک باگ فوری روی شاخه‌ی دیگر را درست کن و برگرد. روی شاخه‌ی feature تغییر نیمه‌کاره بده؛ stash کن؛ به main برو، یک فایل را درست و commit کن؛ برگرد و کارت را از کشو بیاور. ثابت کن همه‌چیز سر جایش است.

دیدن جواب
Terminal window
mkdir -p ~/gitlab/ex2 && cd ~/gitlab/ex2 && git init -q
echo "price = 10" > shop.py && git add shop.py && git commit -qm "Add shop"
git switch -qc feature/cart
echo "cart = [] # half done" >> shop.py
echo "--- وسط کار:"; git status -s
git stash -q -m "cart half done"
git switch -q main
sed -i 's/price = 10/price = 12/' shop.py
git commit -qam "Fix price"
git switch -q feature/cart
git stash pop -q
echo "--- برگشتم:"
git status -s
cat shop.py
git log --oneline --all
خروجی
--- وسط کار:
M shop.py
--- برگشتم:
M shop.py
price = 10
cart = [] # half done
37b8140 Add shop
40a6da8 Fix price
✎ تمرینسخت

دو تغییر نامرتبط داخل یک فایل بده (خط ۲ و خط ۱۸ از یک فایل ۲۰خطی)؛ فقط تغییر خط ۱۸ را با git stash push -p کنار بگذار (جواب‌ها را با printf بده)، ثابت کن فقط تغییر خط ۲ در پوشه ماند، و بعد stash را روی یک شاخه‌ی دیگر apply کن.

دیدن جواب
Terminal window
mkdir -p ~/gitlab/ex3 && cd ~/gitlab/ex3 && git init -q
seq 1 20 > list.txt && git add list.txt && git commit -qm "Add list"
sed -i '2s/.*/two (keep working on this)/' list.txt
sed -i '18s/.*/eighteen (stash this)/' list.txt
echo "--- git stash push -p: اولی را نگیر (n)، دومی را بگیر (y):"
printf 'n\ny\n' | git stash push -p -m "line 18 change" list.txt 2>&1 | tail -3
echo "--- پوشه فقط تغییر خط ۲ را دارد:"
git diff --stat; git diff | grep '^[+-][^+-]'
echo "--- apply روی یک شاخه‌ی دیگر:"
git restore list.txt
git switch -qc other
git stash apply -q
git diff | grep '^[+-][^+-]'
خروجی
--- git stash push -p: اولی را نگیر (n)، دومی را بگیر (y):
20
(2/2) Stash this hunk [y,n,q,a,d,K,g,/,e,?]?
Saved working directory and index state On main: line 18 change
--- پوشه فقط تغییر خط ۲ را دارد:
list.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
-2
+two (keep working on this)
--- apply روی یک شاخه‌ی دیگر:
-18
+eighteen (stash this)
⚡ بررسی سریع

تفاوت git stash pop و git stash apply؟

؟ آزمونک
  1. git stash به‌صورت پیش‌فرض چه چیزی را کنار نمی‌گذارد؟

  2. stash@{0} کدام است؟

  3. git stash pop با conflict تمام شد. stash چه می‌شود؟

  4. stash پشت پرده چیست؟

  5. برای کار چندروزه‌ی نیمه‌کاره که روی کامپیوتر دیگر هم می‌خواهی، بهترین راه؟

  • git stash تغییرهای دنبال‌شده (ویرایش‌شده و staged) را در یک پشته می‌گذارد و پوشه را به آخرین commit برمی‌گرداند؛ git stash pop تازه‌ترین را برمی‌گرداند و برمی‌دارد، apply نگه می‌دارد.
  • git stash push -m "پیام" اسم می‌دهد؛ list، show -p stash@{N}، drop، clear. -u فایل‌های جدید را هم می‌گیرد؛ push -- مسیر و -p بخشی را.
  • git stash branch نام از stash یک شاخه می‌سازد؛ conflict هنگام pop ← stash پاک نمی‌شود، بعد از حل drop کن.
  • پشت پرده stash یک commit است (refs/stash)؛ push نمی‌شود و بکاپ ندارد: برای کار طولانی شاخه‌ی شخصی بساز.
  • پاک‌شده را با هش چاپ‌شده در Dropped ... (هش) و git stash apply هش برمی‌گردانی.
برگه‌ی تقلب این درس
دستورکاری که می‌کند
git stash / git stash push -m "پیام"کنار گذاشتن (با نام)
git stash -uبا فایل‌های جدید
git stash push -- فایل / git stash push -pفقط بخشی
git stash listفهرست کشو
git stash show -p stash@{1}دیدن تغییر یک stash
git stash popبرگردان و بردار
git stash apply stash@{1}برگردان و نگه دار
git stash drop / git stash clearحذف یکی / همه
git stash branch نامساخت شاخه از stash