হ্যাঁ — প্রায় সবসময়ই উদ্ধার সম্ভব। অস্ত্রের নাম: git reflog
আগের আলোচনাগুলোতে বারবার এক লাইনে প্রতিশ্রুতি দিয়ে রেখেছি — "reflog তো আছেই।" এবার পুরো গল্পটা।
প্রথমে শান্ত হোন: commit আসলে "হারায়নি"
প্রথম আলোচনার মানসিক মডেলে ফিরুন। git reset --hard HEAD~2 কী করেছে? শুধু branch pointer-টা পেছনে সরিয়েছে। Commit object-গুলো .git/objects-এ দিব্যি বসে আছে — শুধু তাদের দিকে তাক করা কোনো branch/tag আর নেই। এদের বলে unreachable (dangling) commit।
আগে: A---B---C---D main
পরে: A---B main C---D (এতিম, কিন্তু জীবিত)
Git এতিম object সাথে সাথে মোছে না — garbage collection (git gc) মোছে, এবং সেটাও ডিফল্টে ~৩০–৯০ দিন পরে। অর্থাৎ আপনার হাতে অঢেল সময় — সমস্যা শুধু একটাই: C আর D-এর hash জানা নেই। এখানেই reflog।
git reflog কী
Git প্রতিবার আপনার HEAD যেখানে যেখানে গেছে তার একটা লোকাল ডায়েরি রাখে — প্রতিটা commit, checkout, merge, rebase, reset, cherry-pick — সব। এটাই reference log:
git reflog
b7e2a91 HEAD@{0}: reset: moving to HEAD~2 ← দুর্ঘটনার মুহূর্ত
f4c8d12 HEAD@{1}: commit: Add discount tests ← এটাই হারানো D!
a9b3e56 HEAD@{2}: commit: Add discount service ← হারানো C
b7e2a91 HEAD@{3}: checkout: moving from feature-x to main
...
পড়ার নিয়ম: HEAD@{0} = এইমাত্র, নিচে যত নামবেন তত অতীত। দুর্ঘটনার ঠিক আগের লাইনটা খুঁজুন — ওখানেই আপনার হারানো সম্পদের hash।
উদ্ধার — তিনটা পথ
পথ ১: সব ফিরিয়ে আনা (সবচেয়ে সাধারণ)
git reset --hard f4c8d12 # বা: git reset --hard HEAD@{1}
হ্যাঁ — যে অস্ত্র ডুবিয়েছিল, সেই অস্ত্রই উদ্ধার করে। Reset তো শুধু pointer সরানো; এবার সঠিক দিকে সরালেন। main আবার D-তে, সব আগের মতো।
পথ ২: আগে দেখে নেওয়া (সতর্ক সংস্করণ)
নিশ্চিত না হলে সরাসরি reset না করে আগে পরিদর্শন:
git show f4c8d12 # commit-টা কী ছিল দেখুন
git checkout f4c8d12 # detached HEAD-এ ঘুরে দেখুন (প্রথম আলোচনার সেই state!)
পথ ৩: আলাদা branch-এ উদ্ধার
git branch rescue f4c8d12
এক লাইনে বিপদ শেষ — এখন rescue নামের pointer commit-গুলোকে ধরে রেখেছে, gc আর ছুঁতে পারবে না। তারপর ধীরেসুস্থে merge/cherry-pick করুন।
Branch-specific reflog-ও আছে:
git reflog show feature-x # নির্দিষ্ট branch-এর pointer কোথায় কোথায় ছিল
Rebase-দুর্ঘটনা, ভুল branch delete — সব উদ্ধারেও একই কৌশল।
সততার জায়গা: কী উদ্ধার হয় না
Reset-আলোচনার সেই সতর্কবার্তা এখানে পূর্ণ অর্থ পায়। Reflog শুধু commit-হওয়া জিনিস ফেরাতে পারে, কারণ Git-এর snapshot শুধু commit-এই তৈরি হয়:
| হারানো জিনিস | উদ্ধারযোগ্য? |
|---|---|
| Commit-করা কাজ | ✅ reflog দিয়ে |
Staged (git add করা) কিন্তু commit-না-করা |
⚠️ আংশিক — git fsck --lost-found দিয়ে blob-স্তরে খোঁজা যায়, কষ্টসাধ্য |
| শুধু working directory-তে থাকা এডিট | ❌ চিরতরে — Git কখনো দেখেইনি |
আর দুটো সীমাবদ্ধতা: reflog সম্পূর্ণ লোকাল — সহকর্মীর মেশিনের বা সার্ভারের reflog আপনার কাছে নেই, fresh clone-এ reflog শূন্য থেকে শুরু। আর এন্ট্রিগুলোর মেয়াদ ডিফল্টে ৯০ দিন (unreachable-দের জন্য ৩০)।
এখান থেকেই ব্যবহারিক শিক্ষাটা: ঘন ঘন commit করুন। "নোংরা" WIP commit-ই আপনার সেফটি নেট — আর সেগুলো পরে গুছানোর জন্য rebase -i তো শিখেই ফেলেছেন। Commit-করা কাজ Git-এ হারানো রীতিমতো কঠিন; commit-না-করা কাজের কোনো বীমা নেই।
এক লাইনে (interview-সংস্করণ)
"হ্যাঁ — reset --hard commit মোছে না, শুধু pointer সরায়; commit-গুলো unreachable হয়ে objects-এ থেকেই যায় (gc-র আগ পর্যন্ত, ~৩০–৯০ দিন), আর git reflog হলো HEAD-এর চলাচলের লোকাল ডায়েরি — সেখান থেকে হারানো commit-এর hash নিয়ে git reset --hard <hash> বা git branch rescue <hash> করলেই উদ্ধার; তবে সীমাটাও জানা জরুরি — reflog লোকাল ও মেয়াদি, এবং কখনো commit-ই-হয়নি এমন কাজ কোনো জাদুতেই ফেরে না।"