গাইড

git reflog: হারানো কমিট ও ব্রাঞ্চ কীভাবে ফিরিয়ে আনবেন

হালনাগাদ: · Skillaria.Top

git reflog একটি স্থানীয় জার্নাল, যেখানে Git HEAD ও ব্রাঞ্চের প্রতিটি স্থানান্তর লেখে: কমিট, reset, checkout, rebase, merge। ভুলের আগের এন্ট্রি খুঁজুন, যেমন HEAD@{2}, তারপর git reset --hard HEAD@{2} দিয়ে অবস্থা ফেরান, অথবা git branch rescue HEAD@{2} দিয়ে নতুন ব্রাঞ্চে কমিট রাখুন।

git reflog হলো রেফারেন্স লগ: Git সেখানে HEAD ও ব্রাঞ্চের শেষ বিন্দুর প্রতিটি স্থানান্তর লেখে। কমিট করলেন, ব্রাঞ্চ বদলালেন, reset, rebase বা merge চালালেন, reflog-এ একটি লাইন যোগ হলো, যেখানে আছে হ্যাশ যে অবস্থায় ঠিক তখন ছিল। তাই যে কমিটের ওপর আর কোনো ব্রাঞ্চ দাঁড়ায় না, সেটিও খুঁজে ফেরানো যায়।

এটা জানা জরুরি: reflog স্থানীয়। শুধু আপনার ক্লোনে থাকে, সার্ভারে যায় না, এবং git clone-এ আসে না। তবু নিজের রিপোজিটরিতে এটি ইতিহাস সংক্রান্ত প্রায় যেকোনো ভুল থেকে বাঁচায়।

git reflog-এর আউটপুট পড়া

git reflog
3c4d5e6 (HEAD -> main) HEAD@{0}: reset: moving to HEAD~2
9a8b7c6 HEAD@{1}: commit: ইমেইল যাচাই যোগ করা
1f2e3d4 HEAD@{2}: commit: কনফিগ আলাদা ফাইলে সরানো
3c4d5e6 (HEAD -> main) HEAD@{3}: checkout: moving from feature to main
7b6a5c4 (feature) HEAD@{4}: commit: লগইন ফর্মের খসড়া

প্রতিটি লাইন হলো HEAD-এর একটি অবস্থা, সবচেয়ে নতুনটি ওপরে:

  • হ্যাশ — অপারেশনের পর HEAD যে কমিটে ছিল;
  • HEAD@{n} — "n বার স্থানান্তর আগে HEAD কোথায় ছিল"; এটি একটি পূর্ণাঙ্গ রেফারেন্স, যেকোনো কমান্ডে দেওয়া যায়;
  • বিবরণ — অপারেশনের ধরন (commit, reset, checkout, rebase, merge, pull, cherry-pick, commit (amend)) এবং বিস্তারিত।

উদাহরণে দেখা যায়: "কনফিগ আলাদা ফাইলে সরানো" ও "ইমেইল যাচাই যোগ করা" দুটি কমিট হয়েছিল, তারপর reset HEAD~2 ব্রাঞ্চকে পেছনে সরিয়েছে। git log-এ সেগুলো আর নেই, কিন্তু reflog-এ আছে।

একটি ব্রাঞ্চের reflog

প্রতিটি ব্রাঞ্চের নিজস্ব জার্নাল আছে। অনেকবার ব্রাঞ্চ বদলালে এটি ছোট ও কাজের:

git reflog show feature
git reflog show origin/main   # fetch-এর সময় রিমোট ব্রাঞ্চ কীভাবে বদলেছে

সময় দিয়ে রেফারেন্স

সংখ্যার বদলে সময়ও দেওয়া যায়: Git খুঁজে বের করবে তখন রেফারেন্স কোথায় ছিল:

git show main@{yesterday}
git diff main@{"2 hours ago"} main
git log -g --since="1 day ago" --oneline

git log-এর -g (--walk-reflogs) ফ্ল্যাগ প্যারেন্ট সম্পর্কের বদলে reflog ধরে এগোয়, এবং পরিচিত ফরম্যাট ও ফিল্টার কাজ করে। তারিখসহ দেখতে git reflog --date=iso ভালো।

git reset --hard-এর পর কমিট ফেরানো

সবচেয়ে সাধারণ ঘটনা: ভুল কমিটে git reset --hard চালিয়েছেন, এবং কাজ "হারিয়ে" গেছে। আসলে কমিটগুলো কোথাও যায়নি, শুধু ব্রাঞ্চ থেকে দেখা যাচ্ছে না।

  1. জার্নাল খুলুন: git reflog।
  2. reset: moving to ... লাইনের ঠিক আগের অবস্থা খুঁজুন। উপরের উদাহরণে এটি HEAD@{1}, হ্যাশ 9a8b7c6।
  3. নিশ্চিত হোন এটিই সেই অবস্থা: git log --oneline -5 HEAD@{1}।
  4. ব্রাঞ্চ ফেরান: git reset --hard HEAD@{1}।
git reset --hard HEAD@{1}
HEAD is now at 9a8b7c6 ইমেইল যাচাই যোগ করা

সতর্ক থাকুন: প্রতিটি অপারেশনের পর নম্বর সরে যায়। যেটি ছিল HEAD@{1}, reset-এর পর সেটি HEAD@{2} হবে। তাই নম্বর নয়, হ্যাশ কপি করা নিরাপদ।

গুরুত্বপূর্ণ সীমাবদ্ধতা: reflog শুধু কমিট করা জিনিস রাখে। reset --hard বা git checkout -- ফাইল যে অকমিট পরিবর্তন মুছেছে, তা reflog দিয়ে ফেরানো যায় না। তবে ফাইল যদি git add করা থাকে, তাহলে কিছু আশা থাকে — নিচে দেখুন।

মুছে যাওয়া ব্রাঞ্চ ফেরানো

আপনি git branch -D feature দিয়ে একটি ব্রাঞ্চ মুছেছেন, যেখানে কমিট না-পাঠানো কাজ ছিল। ব্রাঞ্চের নিজস্ব জার্নাল মুছে যায়, কিন্তু HEAD-এর জার্নাল থেকে যায়, এবং HEAD ওই ব্রাঞ্চের কমিটগুলোতে ছিল যখন আপনি ওখানে কাজ করছিলেন।

git reflog | grep feature
7b6a5c4 HEAD@{4}: commit: লগইন ফর্মের খসড়া
3c4d5e6 HEAD@{3}: checkout: moving from feature to main

# ব্রাঞ্চের শেষ কমিট — যেখান থেকে তা ছেড়ে যাওয়া হয়েছিল
git branch feature 7b6a5c4

খুঁজুন checkout: moving from feature to ... লাইনটি: তার ঠিক নিচের এন্ট্রির (অর্থাৎ আগের অবস্থার) হ্যাশ হলো ব্রাঞ্চের শেষ অবস্থা। ব্রাঞ্চ মুছলে Git নিজেও হ্যাশ দেখায়: Deleted branch feature (was 7b6a5c4). — টার্মিনাল খোলা থাকলে সেটুকুই যথেষ্ট।

ব্যর্থ rebase ফেরানো

Rebase কমিট লেখে, এবং কনফ্লিক্ট ভুলভাবে মিটলে ফলাফল আগের চেয়ে খারাপ হতে পারে। Reflog rebase শুরুর আগের অবস্থা রাখে।

git reflog
e1f2a3b HEAD@{0}: rebase (finish): returning to refs/heads/feature
e1f2a3b HEAD@{1}: rebase (pick): লগইন ফর্মের খসড়া
c4d5e6f HEAD@{2}: rebase (start): checkout main
7b6a5c4 HEAD@{3}: commit: লগইন ফর্মের খসড়া

git reset --hard HEAD@{3}

প্রয়োজন rebase (start)-এর ঠিক আগের এন্ট্রি। একটি ছোট পথও আছে: প্রতিটি বিপজ্জনক অপারেশনের (rebase, merge, reset) আগে Git পূর্বের অবস্থা ORIG_HEAD রেফারেন্সে রাখে, তাই rebase-এর ঠিক পরেই git reset --hard ORIG_HEAD কাজ করে। তবে ORIG_HEAD পরের এমন অপারেশনে বদলে যায়, অথচ reflog পুরো ইতিহাস রাখে।

git commit --amend-এর আগের কমিট ফেরানো

--amend কমিট বদলায় না, তার জায়গায় একটি নতুন কমিট বানায়। পুরনোটি reflog-এ থেকে যায়:

git reflog
5a6b7c8 HEAD@{0}: commit (amend): ইমেইল যাচাই যোগ করা
9a8b7c6 HEAD@{1}: commit: ইমেইল যাচাই যোগ করা

git diff HEAD@{1} HEAD          # amend কী বদলেছে
git reset --soft HEAD@{1}       # পুরনো কমিট ফেরানো, amend-এর পরিবর্তন ইনডেক্সে থাকবে

নিরাপদ পুনরুদ্ধার: reset-এর বদলে ব্রাঞ্চ

কোন কমিট দরকার নিশ্চিত না হলে অন্ধভাবে reset --hard করবেন না। একটি উদ্ধার ব্রাঞ্চ বানান: এটি কিছু ভাঙে না এবং খুঁজে পাওয়া কমিটকে মুছে যাওয়া থেকে রক্ষা করে:

git branch rescue HEAD@{5}
git log --oneline rescue
git switch rescue

এরপর rescue-এর কমিটগুলো cherry-pick বা merge করা যায়, এবং সব ঠিক জায়গায় ফিরলে ব্রাঞ্চটি মুছে দেবেন।

reflog কতদিন থাকে

এন্ট্রি চিরস্থায়ী নয়। git gc সেগুলো পরিষ্কার করে, যা Git মাঝে মাঝে নিজে চালায়। সময়সীমা ঠিক করে সেটিংস:

সেটিংডিফল্টঅর্থ
gc.reflogExpire৯০ দিনবর্তমান ব্রাঞ্চ থেকে পৌঁছানো যায় এমন কমিটের এন্ট্রির মেয়াদ
gc.reflogExpireUnreachable৩০ দিনকোনো ব্রাঞ্চ থেকে পৌঁছানো যায় না এমন কমিটের এন্ট্রির মেয়াদ — ঠিক "হারানো" কমিট
gc.pruneExpire২ সপ্তাহকোনো কিছু যাদের দিকে ইশারা করে না, এমন বিচ্ছিন্ন অবজেক্ট কতদিন পরে মুছবে

ব্যবহারিক সিদ্ধান্ত: হারানো কমিট প্রায় এক মাস reflog-এ নিশ্চিতভাবে পাওয়া যায়। ফেরানো না হওয়া পর্যন্ত তাড়াহুড়ো করে git gc --prune=now বা git reflog expire --expire=now --all চালাবেন না।

reflog কাজ না করলে: git fsck

কখনো কমিট reflog-এ থাকে না: যেমন কমিটটি git stash তৈরি করেছিল এবং পরে git stash drop হয়েছে, অথবা পরিবর্তন শুধু ইনডেক্সে ছিল। তখন "ঝুলন্ত" (dangling) অবজেক্ট খুঁজুন:

git fsck --lost-found
dangling commit 2b3c4d5e6f...
dangling blob 8e9f0a1b2c...

git show 2b3c4d5e6f            # বিষয়বস্তু দেখা
git branch recovered 2b3c4d5e6f

মোছা stash-এর ঝুলন্ত কমিটে "WIP on ..." বার্তা থাকে। ঝুলন্ত blob হলো সেই ফাইলের বিষয়বস্তু, যা কখনো git add হয়েছিল কিন্তু কমিট হয়নি। নাম থাকে না, তাই বিষয়বস্তু দেখে চিনতে হয়, তবু reset --hard-এর পর কাজ বাঁচানোর সুযোগ এখানেই। --lost-found ফ্ল্যাগ সেগুলো .git/lost-found/-এ কপি করেও দেয়।

reflog যা পারে না

  • মেশিনের মধ্যে কাজ করে না। সহকর্মী force push করে সার্ভারে আপনার কমিট মুছে দিলে, সেগুলো তাঁর reflog-এ খুঁজুন যার কাছে সেগুলো ছিল: যেমন git reflog show origin/main — যিনি মুছে যাওয়ার আগে fetch করেছিলেন।
  • অকমিট পরিবর্তন দেখে না। তাই ঝুঁকিপূর্ণ অপারেশনের আগে সেরা বীমা: কমিট বা git stash।
  • ইতিহাস চিরকাল রাখে না। ওপরের মেয়াদ প্রযোজ্য, এবং নতুন ক্লোন খালি জার্নাল দিয়ে শুরু করে।

অনুশীলন: ঝুঁকিপূর্ণ কাজের আগে বীমা

reflog উদ্ধার করে, কিন্তু সেখান থেকে ফেরাতে মনোযোগ লাগে: কয়েক ডজন একরকম এন্ট্রির মধ্যে সঠিকটি বেছে নিতে হয়। আগে থেকে চিহ্ন রাখা অনেক সহজ। rebase, বড় reset বা ইতিহাস নিয়ে পরীক্ষার আগে একটি অস্থায়ী ব্রাঞ্চ বা ট্যাগ বানান:

git branch backup/before-rebase
git rebase -i main
# কিছু একটা ভুল হলো
git reset --hard backup/before-rebase
# সব ঠিক হলো
git branch -D backup/before-rebase

এতে একটি কমান্ড খরচ হয় এবং হ্যাশ খোঁজার ঝামেলা থাকে না। reflog থাকে সেই ক্ষেত্রে বিকল্প হিসেবে, যখন বীমার কথা মনে পড়েছে অনেক দেরিতে।

নির্দিষ্ট এন্ট্রি দ্রুত খোঁজা

দীর্ঘ জার্নালে ফিল্টার সাহায্য করে। অপারেশনের ধরন বা কমিটের বার্তার লেখা দিয়ে খুঁজুন, আর সন্দেহ হলে বিষয়বস্তু দেখুন:

git reflog | grep -E "reset|rebase \(start\)"
git log -g --grep="যাচাই" --oneline
git show --stat HEAD@{7}

git log -g-এর --grep reflog-এর কমিট বার্তায় খোঁজে, আর git show --stat দেখায় কোন ফাইল বদলেছে। এতে দরকারি কমিট চেনা যায়, এমনকি বার্তা যদি "wip"-এর মতো অস্পষ্ট হয়।

সারসংক্ষেপ

git reflog Git-এর নিরাপত্তা জাল: আপনার রিপোজিটরিতে গত কয়েক সপ্তাহে যা কমিট হয়েছে, তা খুঁজে ফেরানো যায়। ক্রম সবসময় এক: জার্নাল খোলা, ভুলের আগের এন্ট্রি খোঁজা, git log বা git show দিয়ে যাচাই, তারপর ব্রাঞ্চ বানানো বা reset --hard। restore, reset, revert এবং ভুল থেকে পুনরুদ্ধারের বাস্তব অনুশীলন Git কোর্সে শেখানো হয়।

সাধারণ প্রশ্ন

git reflog-এর সঙ্গে git log-এর পার্থক্য কী?

git log প্যারেন্ট সম্পর্ক ধরে কমিট দেখায় এবং ব্রাঞ্চের ইতিহাস বোঝায়। git reflog দেখায় HEAD ও ব্রাঞ্চ আপনার রিপোজিটরিতে কোথায় কোথায় সরেছে, যার মধ্যে এমন কমিটও আছে যা আর কোনো ব্রাঞ্চের ইতিহাসে নেই।

reflog কি GitHub বা GitLab-এ যায়?

না, reflog শুধু স্থানীয় .git/logs ফোল্ডারে থাকে এবং push বা clone-এ আসে না।

reflog কি পরিষ্কার করা যায়?

হ্যাঁ, git reflog expire --expire=now --all এবং তারপর git gc --prune=now দিয়ে। কিন্তু এরপর হারানো কমিট আর ফেরানো যাবে না।

reflog ফাইলগুলো কোথায় থাকে?

লেখা ফাইলে: .git/logs/HEAD এবং .git/logs/refs/heads/ব্রাঞ্চ-নাম, প্রতিটি স্থানান্তরের জন্য এক লাইন করে।

সার্ভারের bare রিপোজিটরিতে কি reflog কাজ করে?

ডিফল্টে bare রিপোজিটরিতে reflog বন্ধ থাকে (core.logAllRefUpdates), তাই সার্ভারে এর ওপর ভরসা করা যায় না।

ভিডিওতে বিষয়টি শিখুন

“শূন্য থেকে আত্মবিশ্বাসী ডেভেলপার পর্যন্ত গিট” কোর্সে বিষয়টি অনুশীলনসহ বিস্তারিত শেখানো হয়েছে।

কোর্সে যান