git stash: কাজ অসমাপ্ত, কিন্তু জায়গা ছাড়তে হবে
সমস্যাটা যেখান থেকে আসে
আপনি feature branch-এ অর্ধেক কাজ করেছেন — কোড compile-ও হয় না এখনো — হঠাৎ production-এ বাগ, এক্ষুনি hotfix branch-এ যেতে হবে। কিন্তু:
- Commit করবেন? অর্ধসমাপ্ত, ভাঙা কোড commit করতে মন চায় না
- Checkout করবেন? Uncommitted পরিবর্তন নিয়ে branch বদলাতে Git প্রায়ই বাধা দেবে (conflict-এর ঝুঁকিতে)
git stash এই তৃতীয় দরজা: working directory আর staging-এর সব পরিবর্তন তুলে একটা তাকের উপর রেখে দেয়, আপনার working directory ফিরে যায় পরিষ্কার HEAD-এর অবস্থায়।
git stash # সব uncommitted পরিবর্তন তাকে তুলুন
# ... hotfix করুন, deploy করুন ...
git checkout feature-x
git stash pop # তাক থেকে নামিয়ে কাজ চালিয়ে যান
ভেতরে কী — তাক-টা আসলে commit!
চেনা প্যাটার্ন: Git-এ প্রায় সবকিছুই commit। Stash-ও ব্যতিক্রম নয় — ভেতরে ভেতরে Git আপনার working directory আর index-এর snapshot নিয়ে বিশেষ commit বানায়, শুধু সেটা কোনো branch-এ থাকে না — থাকে refs/stash নামের আলাদা reference-এ, stack (LIFO) আকারে:
git stash list
# stash@{0}: WIP on feature-x: a3f5c9d Add discount service ← সর্বশেষ
# stash@{1}: WIP on main: e4f5g6h Update readme
এ কারণেই stash-করা কাজও reflog-দর্শনে "নিরাপদ" — commit হয়ে গেছে মানে Git-এর object store-এ ঢুকে গেছে।
কাজের কয়েকটা রূপ:
git stash push -m "discount WIP - half-done validation" # নাম দিন! পরে চিনবেন
git stash push OrderService.java # শুধু নির্দিষ্ট ফাইল
git stash show -p stash@{0} # ভেতরে কী আছে দেখুন
git stash branch rescue-branch stash@{0} # stash থেকে সরাসরি নতুন branch
শেষেরটা লুকানো রত্ন: stash করার পর মূল branch অনেক এগিয়ে গেলে pop-এ conflict হতে পারে — stash branch তখন যে-commit-এ stash করেছিলেন সেখান থেকেই branch বানিয়ে stash apply করে দেয়, conflict-এর সম্ভাবনা শূন্য।
pop বনাম apply: এক অক্ষরের পার্থক্য, এক দুর্ঘটনার দূরত্ব
দুটোই stash-এর পরিবর্তন working directory-তে ফিরিয়ে আনে। পার্থক্য শুধু তারপর কী হয়:
git stash pop |
git stash apply |
|
|---|---|---|
| পরিবর্তন ফেরায় | ✅ | ✅ |
| Stash তালিকা থেকে মুছে দেয় | ✅ | ❌ — stash থেকে যায় |
| উপমা | তাক থেকে নামিয়ে আনা | তাকের জিনিসের ফটোকপি নেওয়া |
কখন কোনটা:
pop— স্বাভাবিক, এক-জায়গায়-ফেরত workflow। কাজ ফিরে পেলেন, তাক পরিষ্কার।apply— যখন stash-টা রাখতে চান: একই পরিবর্তন একাধিক branch-এ লাগবে (যেমন একটা debug-config দুই জায়গায় পরীক্ষা করবেন), বা ঝুঁকিপূর্ণ অবস্থায় সাবধানি সংস্করণ হিসেবে।
সূক্ষ্ম কিন্তু জরুরি আচরণ: pop-এ যদি conflict হয় (stash-এর পর ওই ফাইলগুলো বদলে গেছে), Git পরিবর্তন apply করে marker বসাবে ঠিকই — কিন্তু stash-টা মুছবে না। এটা আসলে সুরক্ষা: conflict-সমাধান ভজঘট হলেও মূল stash অক্ষত। conflict মিটিয়ে নিজে git stash drop করতে হবে।
আর ভুল করে দরকারি stash pop/drop করে ফেললে? এতক্ষণের অভ্যাসমতো — ঘাবড়াবেন না, stash তো commit-ই ছিল:
git fsck --unreachable | grep commit # এতিম stash-commit খুঁজুন
git stash apply <hash> # বা cherry-pick জাতীয় উদ্ধার
(এরা branch-reflog-এ থাকে না বলে fsck-ই ভরসা — আরেকটা কারণ গুরুত্বপূর্ণ কাজে stash নয়, আসল commit।)
Untracked ফাইল: ডিফল্টে stash-এর চোখের আড়ালে
এটা সবচেয়ে বড় stash-বিভ্রান্তি, আর interview-প্রশ্নের তৃতীয় অংশ। ডিফল্ট git stash নেয় শুধু tracked ফাইলের পরিবর্তন — staged + unstaged। কিন্তু:
- নতুন বানানো, কখনো
git addহয়নি এমন ফাইল → stash ছোঁবেই না, working directory-তে পড়ে থাকবে .gitignore-করা ফাইল → অবশ্যই না
ফাঁদটা বাস্তবে যেভাবে ধরা দেয়: নতুন class-ফাইল সহ feature-এর কাজ stash করলেন, branch বদলালেন — নতুন ফাইলটা এই branch-এও ঘুরছে, ভুল branch-এর build-এ ঢুকে বিভ্রান্তি।
সমাধান — দুই স্তরের flag:
git stash -u # --include-untracked: নতুন ফাইলও নাও
git stash -a # --all: ignored ফাইল সহ সব — খুব কমই লাগে
| কী stash হয় | (ডিফল্ট) | -u |
-a |
|---|---|---|---|
| Tracked পরিবর্তন | ✅ | ✅ | ✅ |
| Untracked নতুন ফাইল | ❌ | ✅ | ✅ |
| Ignored (build/, .env...) | ❌ | ❌ | ✅ |
ব্যবহারিক অভ্যাস: git stash -u-ই আপনার ডিফল্ট হোক — "নতুন ফাইল রয়ে গেছে" বিভ্রান্তিটা আর হবে না। -a সাবধানে — target/, node_modules জাতীয় দৈত্য stash-এ ঢুকিয়ে অকারণ ভার।
আরেকটা কাজের flag উল্টোদিকে: git stash --keep-index — staged জিনিস রেখে শুধু unstaged stash করে; "commit-এর জন্য যা সাজিয়েছি তা ঠিক আছে কি না একা টেস্ট করি" — এই কাজে দারুণ।
Stash বনাম WIP-commit — সৎ তুলনা
সিনিয়র উত্তরে এটুকু যোগ করলে পূর্ণতা পায়: stash-এর প্রতিযোগী হলো স্রেফ একটা WIP commit (git commit -m "WIP" — পরে reset --soft বা rebase -i fixup দিয়ে গোছানো, যেগুলো আগেই শিখেছি)। Stash জিতবে দ্রুত, ক্ষণস্থায়ী context-switch-এ; WIP-commit জিতবে কাজটা কয়েক ঘণ্টার বেশি রাখতে হলে — কারণ commit branch-এর সাথে বাঁধা, নামধারী, push-যোগ্য (ব্যাকআপ!), আর stash-stack-এ stash@{3}-এর মতো নামহীন জঞ্জাল জমে হারিয়ে যাওয়ার জিনিস। Stash তাক, commit সিন্দুক।
এক লাইনে (interview-সংস্করণ)
"Stash হলো uncommitted কাজ (staged+unstaged) branch-নিরপেক্ষ একটা LIFO তাকে তুলে working directory পরিষ্কার করা — ভেতরে এগুলো আসলে refs/stash-এ রাখা বিশেষ commit; pop = apply + তালিকা থেকে মোছা (তবে conflict হলে মোছে না — সুরক্ষা), apply = কপি রেখে লাগানো — একাধিক জায়গায় লাগবে বা সাবধান থাকতে চাইলে apply; আর ডিফল্ট stash নতুন untracked ফাইল নেয়ই না — সেজন্য -u, ignored-সহ চাইলে -a; দীর্ঘমেয়াদে রাখতে হলে অবশ্য stash নয়, WIP-commit।"