BackendServerGit

গিট (Git) সিনারিও এবং তার সমাধান

Rakib Hasan63 min read
Table of Contents

গিট (Git) হলো ডেভেলপারদের জন্য সবচেয়ে প্রয়োজনীয় টুল—এটি মূলত আপনার কোডের “টাইম মেশিন”। প্রজেক্টের কোডে কখন কী পরিবর্তন হয়েছে তা মনে রাখা এবং টিমের সাথে কাজ করার জন্য প্রফেশনালরা এটি ব্যবহার করেন।

নিচে বিগিনার থেকে অ্যাডভান্সড লেভেল পর্যন্ত Git-এর প্রয়োজনীয় কমান্ড, প্রফেশনাল সিনারিও এবং ইউজ-কেস (Use Case) সহজ বাংলায় আলোচনা করা হলো:

📘 আরও গভীরে যেতে চান? এই পোস্টটা quick-reference হিসেবে ভালো, কিন্তু Git-এর internals, প্রতিটা কমান্ডের ভেতরের মেকানিজম, আর structured লার্নিং পাথ চাইলে দেখুন আমাদের সম্পূর্ণ Git মাস্টারি জার্নি — শূন্য থেকে এক্সপার্ট পর্যন্ত ১৩টা ফেজে ভাগ করা।

১. বিগিনার লেভেল: বেসিক ওয়ার্কফ্লো (একা কাজ করার ক্ষেত্রে)

আপনি যখন একা কোনো প্রজেক্টে কাজ শুরু করেন, তখন এই কমান্ডগুলো সবচেয়ে বেশি লাগে।

📌 কমান্ডসমূহ:

  • git init: নতুন প্রজেক্ট ফোল্ডারকে গিট রিপোজিটরি হিসেবে তৈরি করতে। কেন/ইন্টারনালি: এটি ফোল্ডারের ভেতরে একটা .git সাবডিরেক্টরি বানায়, যার ভেতরে থাকে objects (সব ফাইলের কনটেন্ট কমপ্রেসড ব্লব হিসেবে), refs (ব্রাঞ্চ ও ট্যাগের পয়েন্টার) আর HEAD (আপনি এখন কোন ব্রাঞ্চে আছেন তার রেফারেন্স)। পুরো রিপোজিটরির ডেটাবেস এই একটা ফোল্ডারেই থাকে, তাই .git ডিলিট করলে সম্পূর্ণ হিস্ট্রি হারিয়ে যায়।
  • git clone <url>: গিটহাব (GitHub) বা অন্য কোনো রিমোট সার্ভার থেকে প্রজেক্ট ডাউনলোড করতে। কেন/ইন্টারনালি: clone আসলে পুরো .git ডিরেক্টরি (সব কমিট, ব্রাঞ্চ, অবজেক্ট) কপি করে আনে, তারপর লেটেস্ট কমিট থেকে working directory তৈরি করে — তাই ক্লোন করা রিপোতেও পুরো হিস্ট্রি লোকালি থাকে, ইন্টারনেট ছাড়াই git log চালানো যায়।
  • git status: কোন ফাইলগুলোতে পরিবর্তন হয়েছে, তা দেখতে। কেন/ইন্টারনালি: এটি working directory, staging area (index) আর সর্বশেষ কমিটের (HEAD) মধ্যে তুলনা করে পার্থক্য দেখায়।
  • git add <file> বা git add .: ফাইলগুলোকে কমিট করার জন্য স্টেজিং এরিয়ায় (Staging Area) নিতে। কেন/ইন্টারনালি: add ফাইলের কনটেন্ট থেকে একটা blob অবজেক্ট বানিয়ে index-এ যোগ করে — কমিট না করা পর্যন্ত এটা শুধু “পরের কমিটে কী কী যাবে” তার একটা তালিকা, আসল হিস্ট্রিতে কিছু যোগ হয় না।
  • git commit -m "Message": পরিবর্তনগুলো একটি মেসেজসহ সেভ বা রেকর্ড করতে। কেন/ইন্টারনালি: commit index-এ থাকা স্ন্যাপশট থেকে একটা নতুন commit অবজেক্ট বানায়, যেটা আগের commit-কে parent হিসেবে পয়েন্ট করে, আর বর্তমান ব্রাঞ্চের ref নতুন commit-এ সরে যায়। টিপ: প্রতিটা ছোট, লজিক্যাল পরিবর্তনের পর কমিট করুন — বড় একটা “mega commit”-এর চেয়ে ছোট কমিট history পড়া ও revert করা অনেক সহজ করে।
  • git push: আপনার লোকাল পিসির কমিট করা কোড গিটহাবে আপলোড করতে। কেন/ইন্টারনালি: লোকাল ব্রাঞ্চে থাকা কমিটগুলো (যা রিমোটে নেই) রিমোট সার্ভারে পাঠিয়ে রিমোট ব্রাঞ্চের ref আপডেট করে।
  • git pull: গিটহাব থেকে নতুন আপডেট আপনার লোকাল পিসিতে নামিয়ে আনতে। কেন/ইন্টারনালি: আসলে এটা দুটো কমান্ডের সমষ্টি — আগে git fetch (রিমোটের নতুন কমিট লোকালে নামায়) তারপর git merge (বর্তমান ব্রাঞ্চের সাথে মিলিয়ে দেয়)।

💡 প্রফেশনাল সিনারিও (Use Case):

সিনারিও: আপনি একটি নতুন পোর্টফোলিও ওয়েবসাইট বানাচ্ছেন।

অ্যাকশন: ফোল্ডারে ঢুকে git init করে গিট চালু করলেন। কিছু কোড লেখার পর git add . দিয়ে ফাইলগুলো সিলেক্ট করলেন এবং git commit -m "Initial commit with index.html" দিয়ে সেভ করলেন। সবশেষে git push দিয়ে কোডগুলো গিটহাবে ব্যাকআপ রাখলেন।

🔎 বাস্তব টিপ: কাজ শুরু করার প্রথম দিনেই .gitignore ফাইল বানিয়ে নিন (যেমন node_modules/, .env), নাহলে পরে ভারী বা sensitive ফাইল ট্র্যাক হয়ে গেলে সেগুলো হিস্ট্রি থেকে বের করা অনেক ঝামেলার কাজ (দেখুন ক্যাটাগরি ৫ ও ১০)।

২. ইন্টারমিডিয়েট লেভেল: ব্রাঞ্চিং এবং কোলাবোরেশন (টিমের সাথে কাজ)

টিমের সাথে কাজ করার সময় মূল কোড (Main/Master branch) যাতে নষ্ট না হয়, সেজন্য ব্রাঞ্চ (Branch) তৈরি করে কাজ করতে হয়।

📌 কমান্ডসমূহ:

  • git branch: প্রজেক্টে কী কী ব্রাঞ্চ আছে তা দেখতে। কেন/ইন্টারনালি: এটি .git/refs/heads/ ডিরেক্টরিতে থাকা প্রতিটা ফাইল (প্রতিটা ব্রাঞ্চ একটা ফাইল) লিস্ট করে দেখায়।
  • git branch <branch-name>: নতুন ব্রাঞ্চ তৈরি করতে। কেন/ইন্টারনালি: refs/heads/<branch-name> নামে একটা নতুন ছোট ফাইল বানায় যেটা বর্তমান commit-এর হ্যাশ ধরে রাখে — তাই ব্রাঞ্চ তৈরি করা প্রায় ইনস্ট্যান্ট এবং সস্তা (কোনো ফাইল কপি হয় না)।
  • git switch <branch-name> বা git checkout: এক ব্রাঞ্চ থেকে অন্য ব্রাঞ্চে যেতে। কেন/ইন্টারনালি: HEAD-কে অন্য ব্রাঞ্চ রেফারেন্সে পয়েন্ট করায় এবং working directory-র ফাইলগুলো সেই ব্রাঞ্চের স্ন্যাপশট অনুযায়ী রিপ্লেস করে দেয়। সতর্কতা: uncommitted পরিবর্তন থাকলে switch/checkout সেগুলো ওভাররাইট করে ফেলতে পারে, তাই আগে git status চেক করুন বা git stash করে নিন।
  • git checkout -b <branch-name>: একসাথে নতুন ব্রাঞ্চ তৈরি করা এবং সেই ব্রাঞ্চে চলে যাওয়া। কেন/ইন্টারনালি: উপরের দুটো ধাপ (branch তৈরি + switch) একসাথে করে।
  • git merge <branch-name>: অন্য একটি ব্রাঞ্চের কাজ আপনার বর্তমান ব্রাঞ্চের সাথে যুক্ত (Merge) করতে। কেন/ইন্টারনালি: দুই ব্রাঞ্চের কমন পূর্বপুরুষ (merge base) বের করে দুই দিকের পরিবর্তন combine করে — সম্ভব হলে fast-forward (শুধু pointer সরানো), নাহলে দুটো parent-সহ একটা নতুন merge commit তৈরি করে।
  • git log: আগের সব কমিটের হিস্ট্রি দেখতে। কেন/ইন্টারনালি: HEAD থেকে শুরু করে parent-child লিংক অনুসরণ করে পেছনের দিকে হেঁটে commit history প্রিন্ট করে।

💡 প্রফেশনাল সিনারিও (Use Case):

সিনারিও: আপনি টিমের মূল প্রজেক্টে “লগিন সিস্টেম” তৈরি করবেন।

অ্যাকশন: আপনি সরাসরি main ব্রাঞ্চে কোড না লিখে git checkout -b feature-login দিয়ে একটি নতুন ব্রাঞ্চ খুললেন। সেখানে লগিনের কোড লিখে কমিট করলেন। কাজ শেষ হলে main ব্রাঞ্চে ফিরে গিয়ে git merge feature-login দিয়ে লগিন সিস্টেমটি মূল প্রজেক্টের সাথে যুক্ত করে দিলেন।

🔎 বাস্তব টিপ: ব্রাঞ্চের নাম বর্ণনামূলক রাখুন (যেমন feature-login, fix-payment-bug) — টিমের সবাই GitHub-এর ব্রাঞ্চ লিস্টে গেলেই বুঝে যাবে কে কী নিয়ে কাজ করছে।

৩. অ্যাডভান্সড লেভেল: ভুল সংশোধন এবং হিস্ট্রি ম্যানিপুলেশন

কাজ করতে গিয়ে ভুল হলে বা ইমার্জেন্সি কোনো টাস্ক চলে এলে এই কমান্ডগুলো জীবন বাঁচায়।

📌 কমান্ডসমূহ:

  • git stash: আপনি একটি কাজ অর্ধেক করেছেন (কমিট করার মতো অবস্থায় নেই), এমন সময় অন্য একটি জরুরি কাজ এলো। তখন বর্তমান কাজগুলো টেম্পোরারি লুকিয়ে রাখতে git stash ব্যবহার হয়। পরে ফিরে এসে git stash pop দিলে আগের অসমাপ্ত কাজ আবার ফেরত পাওয়া যায়। কেন/ইন্টারনালি: stash working directory ও index-এর বর্তমান অবস্থা নিয়ে বিশেষ কিছু commit বানিয়ে refs/stash-এ রাখে, তারপর working directory-কে HEAD-এর অবস্থায় ফিরিয়ে ক্লিন করে দেয়।
  • git revert <commit-hash>: আগের কোনো কমিট মুছে না ফেলে, সেই কমিটের পরিবর্তনগুলোকে বাতিল করে নতুন একটি কমিট তৈরি করে (এটি সবচেয়ে নিরাপদ)। কেন/ইন্টারনালি: টার্গেট commit-এর পরিবর্তনের উল্টো (inverse patch) apply করে নতুন commit বানায় — পুরনো history অক্ষত থাকে বলে shared/pushed ব্রাঞ্চেও নিরাপদে ব্যবহার করা যায়।
  • git reset --hard <commit-hash>: নির্দিষ্ট কমিটের পরের সব কমিট এবং পরিবর্তন স্থায়ীভাবে মুছে ফেলতে (বিপজ্জনক, সাবধানে ব্যবহার করতে হয়)। কেন/ইন্টারনালি: branch ref-কে টার্গেট commit-এ সরিয়ে দেয় এবং index ও working directory দুটোই সেই commit অনুযায়ী ওভাররাইট করে — তাই uncommitted changes হারিয়ে যায়। সতর্কতা: এটি history-rewriting/destructive অপারেশন। পুশ করা ব্রাঞ্চে এটা ব্যবহার করলে টিমের বাকিদের সাথে হিস্ট্রি মিসম্যাচ হয়ে যাবে; ভুলে করে ফেললে git reflog দিয়ে হারানো commit ফেরত পাওয়া যায় (যতক্ষণ না garbage collect হয়)।
  • git rebase: একটি ব্রাঞ্চের কমিটগুলোকে অন্য ব্রাঞ্চের একদম উপরে বসিয়ে দেওয়া, যাতে হিস্ট্রি একদম পরিষ্কার (Linear) থাকে। কেন/ইন্টারনালি: বর্তমান ব্রাঞ্চের কমিটগুলো সাময়িকভাবে সরিয়ে রেখে টার্গেট ব্রাঞ্চের টিপ থেকে একে একে replay (নতুন commit হিসেবে re-apply) করে — ফলে commit hash বদলে যায়। সতর্কতা: ইতিমধ্যে পুশ হওয়া/অন্যরা ব্যবহার করছে এমন শেয়ার্ড ব্রাঞ্চে rebase করলে হিস্ট্রি ডাইভার্জ করে যায়, তাই “public” ব্রাঞ্চে rebase এড়িয়ে চলুন।

💡 প্রফেশনাল সিনারিও (Use Case):

সিনারিও: আপনি একটি ফিচারের কাজ করছেন, হঠাৎ বস বলল প্রোডাকশনে একটি বাগ (Bug) ফিক্স করতে হবে এখনি।

অ্যাকশন: আপনি বর্তমান অসমাপ্ত কাজ git stash করে রাখলেন। তারপর main ব্রাঞ্চে গিয়ে বাগ ফিক্স করে পুশ করলেন। এরপর আবার নিজের ব্রাঞ্চে ফিরে এসে git stash pop দিয়ে আগের কাজ সেখান থেকেই শুরু করলেন।

🔎 বাস্তব টিপ: reset --hard বা rebase চালানোর আগে যদি এতটুকুও সন্দেহ থাকে, আগে git branch backup-before-reset দিয়ে বর্তমান অবস্থার একটা ব্যাকআপ ব্রাঞ্চ বানিয়ে নিন — এক সেকেন্ডের কাজ, কিন্তু ভুল হলে সরাসরি ওই ব্রাঞ্চে ফিরে যাওয়া যায়।

৪. প্রো লেভেল: ডিবাগিং এবং মেইনটেন্যান্স (সিনিয়র ডেভেলপার)

বড় প্রজেক্ট মেইনটেইন করা এবং কে কোথায় বাগ তৈরি করেছে তা খুঁজে বের করার জন্য।

📌 কমান্ডসমূহ:

  • git cherry-pick <commit-hash>: অন্য কোনো ব্রাঞ্চ থেকে শুধুমাত্র নির্দিষ্ট একটি কমিট (সবগুলো নয়) কপি করে আপনার বর্তমান ব্রাঞ্চে নিয়ে আসতে। কেন/ইন্টারনালি: টার্গেট commit-এর diff (patch) বের করে সেটা বর্তমান HEAD-এর উপর apply করে নতুন commit বানায় — একই কনটেন্ট কিন্তু ভিন্ন commit hash, কারণ parent আলাদা।
  • git blame <filename>: ফাইলের প্রতিটি লাইন কে, কবে এবং কোন কমিটে লিখেছে তা বের করতে। কেন/ইন্টারনালি: প্রতিটা লাইনের জন্য history পেছনের দিকে হেঁটে বের করে সবশেষ কোন commit-এ লাইনটা শেষবার পরিবর্তিত হয়েছিল।
  • git bisect: একটি বাগ কোন নির্দিষ্ট কমিটের কারণে তৈরি হয়েছে, তা বাইনারি সার্চ (Binary Search) এর মাধ্যমে দ্রুত খুঁজে বের করতে। কেন/ইন্টারনালি: known-good আর known-bad commit-এর মাঝখানে বাইনারি সার্চ করে প্রতিবার একটা commit checkout করিয়ে “bug আছে কি নেই” জিজ্ঞেস করে — হাজার কমিট হলেও মাত্র log₂(n) ধাপেই কালপ্রিট commit বের হয়ে যায়।

💡 প্রফেশনাল সিনারিও (Use Case):

সিনারিও: ওয়েবসাইটে একটি বড় সমস্যা দেখা দিয়েছে, কিন্তু কেউ জানে না কোডের কোন অংশে বা কখন এই ভুলটা হয়েছে।

অ্যাকশন: আপনি git blame index.js চালিয়ে দেখলেন এই ভুলের কোডটি কে লিখেছে। তারপর দেখলেন অন্য একটি ব্রাঞ্চে এই সমস্যা সমাধানের জন্য একটি হট-ফিক্স (Hotfix) কমিট করা আছে। আপনি git cherry-pick করে শুধু সেই সমাধানের কমিটটি আপনার main ব্রাঞ্চে নিয়ে আসলেন।

🔎 বাস্তব টিপ: git blame-এ কারো নাম দেখেই তাকে দোষ দেবেন না — অনেক সময় সেই লাইনটা শুধু ইনডেন্টেশন/ফরম্যাটিং কমিটের কারণে তার নামে দেখাতে পারে। git blame -w (whitespace ignore) বা -C (code movement track) ব্যবহার করে আসল উৎস কমিট বের করুন।


১০০টি গিট (Git) সিনারিও এবং তার সমাধান নিচে দেওয়া হলো। সহজে খুঁজে পাওয়ার জন্য এগুলোকে বিভিন্ন ক্যাটাগরিতে ভাগ করা হয়েছে। বিগিনার থেকে শুরু করে অ্যাডভান্সড প্রো লেভেল—সবকিছুই এখানে আছে। প্রতিটা সিনারিওর জন্য থাকছে ঘটনাটা কী, ভেতরে গিট আসলে কী করছে (ইন্টারনাল মেকানিজম), সমাধান কমান্ড, একটা বাস্তব উদাহরণ, আর যেখানে দরকার সেখানে একটা সতর্কতা।

🛠️ ক্যাটাগরি ১: প্রজেক্ট সেটআপ ও কনফিগারেশন (১-১০)

১. নতুন প্রজেক্টে Git চালু করা

সিনারিও (কী ঘটেছে): আপনি একটি নতুন প্রজেক্ট ফোল্ডার তৈরি করেছেন এবং সেখানে গিট চালু (initialize) করতে চান, যাতে ভবিষ্যতের সব পরিবর্তন ট্র্যাক করা যায়।

কেন এটা হয় / ইন্টারনাল মেকানিজম: git init চালানোর সাথে সাথে গিট বর্তমান ফোল্ডারে একটা .git সাবডিরেক্টরি তৈরি করে। এর ভেতরে থাকে objects/ (ভবিষ্যতে সব ফাইল কনটেন্ট এখানে কমপ্রেসড ব্লব হিসেবে জমা হবে), refs/heads/ (ব্রাঞ্চ পয়েন্টার), আর HEAD ফাইল (এখন কোন ব্রাঞ্চ অ্যাক্টিভ তার রেফারেন্স, ডিফল্টভাবে refs/heads/main কে পয়েন্ট করে)। এই মুহূর্তে কোনো commit object তৈরি হয় না — শুধু কাঠামোটা বসানো হয়।

সমাধান:

git init
# নির্দিষ্ট ব্রাঞ্চ নাম দিয়ে শুরু করতে চাইলে:
git init -b main

বাস্তব উদাহরণ: একজন ফ্রিল্যান্স ডেভেলপার নতুন একটা ল্যান্ডিং পেজ প্রজেক্টের ফোল্ডার বানিয়ে টার্মিনালে ঢুকে প্রথমেই git init চালান, তারপর .gitignore বানিয়ে node_modules বাদ দেন — যাতে প্রথম কমিট থেকেই রিপোজিটরি পরিষ্কার থাকে।

২. GitHub থেকে প্রজেক্ট ক্লোন করা

সিনারিও (কী ঘটেছে): গিটহাব থেকে কোনো প্রজেক্ট পিসিতে নামাতে চান, যাতে লোকালি কাজ করতে পারেন।

কেন এটা হয় / ইন্টারনাল মেকানিজম: git clone রিমোট রিপোজিটরির পুরো .git ডেটাবেস (সব commit, branch, tag, object) ডাউনলোড করে আনে, তারপর ডিফল্ট ব্রাঞ্চের লেটেস্ট commit থেকে working directory তৈরি করে দেয়। এটি স্বয়ংক্রিয়ভাবে origin নামে একটা remote-ও যোগ করে দেয়, যা মূল সোর্স রিপোজিটরিকে পয়েন্ট করে।

সমাধান:

git clone <repo-url>

বাস্তব উদাহরণ: নতুন একজন টিম মেম্বার প্রথম দিনে অফিসের ল্যাপটপে git clone https://github.com/company/project.git চালিয়ে পুরো কোডবেস ও তার সম্পূর্ণ কমিট হিস্ট্রি একসাথে নামিয়ে ফেলেন, ফলে ইন্টারনেট ছাড়াই আগের সব পরিবর্তন git log-এ দেখতে পারেন।

৩. Git ইউজারনেম সেট করা

সিনারিও (কী ঘটেছে): গিট-এ আপনার নাম সেট করতে চান, যাতে আপনার করা প্রতিটা কমিটে সেই নামটা দেখায়।

কেন এটা হয় / ইন্টারনাল মেকানিজম: প্রতিটা commit object-এ author ও committer হিসেবে name/email এমবেড করা থাকে। git config এই তথ্য ~/.gitconfig (global) বা .git/config (শুধু এই রিপোতে, local) ফাইলে সেভ করে রাখে, আর কমিট করার সময় গিট সেখান থেকে পড়ে নেয়।

সমাধান:

git config --global user.name "আপনার নাম"

বাস্তব উদাহরণ: একটা নতুন ল্যাপটপে গিট ইনস্টল করার পরপরই একজন ডেভেলপার প্রথম কমিট করতে গিয়ে গিট এরর দেখান (“Please tell me who you are”) — কারণ কনফিগ সেট করা হয়নি; user.name সেট করার পর সমস্যা মিটে যায়।

৪. Git ইমেইল সেট করা

সিনারিও (কী ঘটেছে): গিট-এ আপনার ইমেইল সেট করতে চান, যাতে GitHub আপনার কমিটকে সঠিক প্রোফাইলের সাথে যুক্ত করতে পারে।

কেন এটা হয় / ইন্টারনাল মেকানিজম: GitHub প্রতিটা commit-এর author email-কে আপনার GitHub অ্যাকাউন্টের ভেরিফায়েড ইমেইলের সাথে ম্যাচ করে দেখায় কমিটটা কার — ইমেইল না মিললে কমিট “unverified” বা অচেনা অথরের নামে দেখায়।

সমাধান:

git config --global user.email "আপনার ইমেইল"

বাস্তব উদাহরণ: একজন ডেভেলপার লক্ষ্য করলেন তার সব কমিট GitHub প্রোফাইল ছবির বদলে একটা ধূসর আইকন দেখাচ্ছে — কারণ কমিট ইমেইল আর GitHub অ্যাকাউন্ট ইমেইল আলাদা ছিল; user.email ঠিক করার পর নতুন কমিট থেকে প্রোফাইলের সাথে ঠিকভাবে লিংক হয়।

৫. প্রজেক্টের বর্তমান স্ট্যাটাস দেখা

সিনারিও (কী ঘটেছে): প্রজেক্টের বর্তমান অবস্থা (কোন ফাইল মডিফাই হয়েছে) দেখতে চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: git status তিনটা জায়গা তুলনা করে — working directory, staging area (index), আর HEAD commit। যে ফাইলগুলোর কনটেন্ট এই তিন জায়গায় আলাদা, সেগুলোকে modified/staged/untracked হিসেবে দেখায়।

সমাধান:

git status
git status -s   # সংক্ষিপ্ত ফরম্যাটে

বাস্তব উদাহরণ: কোড লেখার মাঝপথে একজন ডেভেলপার ভুলে যান ঠিক কোন কোন ফাইল স্পর্শ করেছেন — git status চালিয়ে দেখেন তিনটা ফাইল modified আর একটা নতুন ফাইল untracked অবস্থায় আছে, তারপর ঠিক করে সেগুলো স্টেজ করেন।

৬. নির্দিষ্ট ফোল্ডারে ক্লোন করা

সিনারিও (কী ঘটেছে): নির্দিষ্ট কোনো ফোল্ডারে গিট ক্লোন করতে চান, ডিফল্ট রিপো-নামের ফোল্ডারে নয়।

কেন এটা হয় / ইন্টারনাল মেকানিজম: clone কমান্ডের দ্বিতীয় আর্গুমেন্ট working directory-র লোকেশন নির্ধারণ করে — গিট ডাটাবেস আর ফাইল স্ট্রাকচার একই থাকে, শুধু টার্গেট ফোল্ডারের নাম/পাথ বদলায়।

সমাধান:

git clone <repo-url> <folder-name>

বাস্তব উদাহরণ: একজন ডেভেলপার একই ব্যাকএন্ড রিপো দুইবার আলাদা ফোল্ডারে ক্লোন করেন (backend-main আর backend-hotfix) যাতে দুইটা আলাদা ব্রাঞ্চে একসাথে কাজ করতে পারেন, একটাতে switch করার জন্য অন্যটার কাজ থামাতে না হয়।

৭. রিমোট URL চেক করা

সিনারিও (কী ঘটেছে): রিমোট রিপোজিটরির (GitHub) URL চেক করতে চান — কোন সার্ভারের সাথে আপনার লোকাল রিপো কানেক্টেড তা নিশ্চিত হতে।

কেন এটা হয় / ইন্টারনাল মেকানিজম: রিমোটের নাম আর URL .git/config ফাইলে [remote "origin"] সেকশনে সেভ থাকে; git remote -v শুধু সেই কনফিগ পড়ে দেখায়, নেটওয়ার্ক কল করে না।

সমাধান:

git remote -v

বাস্তব উদাহরণ: একজন ডেভেলপার push করার আগে নিশ্চিত হতে চান তিনি ভুল করে personal fork-এ push করছেন না, তাই git remote -v চালিয়ে origin-এর URL যাচাই করে নেন।

৮. লোকাল প্রজেক্টে রিমোট লিংক করা

সিনারিও (কী ঘটেছে): লোকাল প্রজেক্টের সাথে গিটহাবের নতুন রিপোজিটরি লিংক করতে চান (যেমন git init দিয়ে শুরু করা প্রজেক্ট)।

কেন এটা হয় / ইন্টারনাল মেকানিজম: এই কমান্ড .git/config-এ একটা নতুন [remote "origin"] এন্ট্রি যোগ করে, যেখানে URL সেভ থাকে — এরপর থেকে git push/git pull ডিফল্টভাবে এই URL ব্যবহার করবে।

সমাধান:

git remote add origin <repo-url>

বাস্তব উদাহরণ: একজন ডেভেলপার প্রথমে লোকালি git init করে কিছুদিন কাজ করার পর GitHub-এ একটা খালি রিপো বানিয়ে git remote add origin ... দিয়ে লিংক করেন, তারপর প্রথমবার push করে পুরনো লোকাল হিস্ট্রি ব্যাকআপ রাখেন।

৯. রিমোট URL পরিবর্তন করা

সিনারিও (কী ঘটেছে): রিমোট URL পরিবর্তন করতে চান (ভুল লিংকে কানেক্ট করলে, বা HTTPS থেকে SSH-এ সরতে চাইলে)।

কেন এটা হয় / ইন্টারনাল মেকানিজম: এটি .git/config-এ থাকা বিদ্যমান origin-এর URL ওভাররাইট করে, নতুন কোনো remote এন্ট্রি তৈরি করে না — commit history বা branch-এ কোনো প্রভাব পড়ে না।

সমাধান:

git remote set-url origin <new-url>

বাস্তব উদাহরণ: একটা টিম HTTPS-এর বদলে SSH-key ভিত্তিক অথেন্টিকেশনে সরে যাওয়ার সিদ্ধান্ত নেয়; প্রতিটা ডেভেলপার git remote set-url origin git@github.com:company/project.git চালিয়ে বারবার পাসওয়ার্ড চাওয়ার ঝামেলা থেকে মুক্তি পান।

১০. সব কনফিগারেশন লিস্ট দেখা

সিনারিও (কী ঘটেছে): আপনার গিট কনফিগারেশনের সব সেটিং (নাম, ইমেইল, alias, এডিটর ইত্যাদি) এক জায়গায় দেখতে চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: গিটের কনফিগ তিন স্তরে থাকে — system, global (~/.gitconfig), আর local (.git/config) — পরেরটা আগেরটাকে override করে। --list তিনটা স্তর মার্জ করে চূড়ান্ত effective value দেখায়।

সমাধান:

git config --list
git config --list --show-origin   # কোন ফাইল থেকে কোন সেটিং এসেছে তাও দেখায়

বাস্তব উদাহরণ: একজন ডেভেলপার নতুন অফিসের পিসিতে অদ্ভুত alias আচরণ দেখে বুঝতে পারছিলেন না সমস্যা কোথায়; git config --list --show-origin চালিয়ে দেখেন কোম্পানির shared config ফাইলে একটা পুরনো alias override করে রাখা আছে।

📝 ক্যাটাগরি ২: স্টেজিং এবং কমিট করা (১১-২০)

১১. নির্দিষ্ট ফাইল স্টেজ করা

সিনারিও (কী ঘটেছে): একটি নির্দিষ্ট ফাইল কমিট করার জন্য রেডি (Stage) করতে চান, অন্য ফাইলগুলো বাদ দিয়ে।

কেন এটা হয় / ইন্টারনাল মেকানিজম: git add <file> ফাইলের বর্তমান কনটেন্ট থেকে একটা blob অবজেক্ট তৈরি করে .git/objects-এ জমা রাখে এবং index ফাইলে সেই ফাইলের এন্ট্রি আপডেট করে — working directory বা HEAD-এ কোনো পরিবর্তন হয় না।

সমাধান:

git add index.html

বাস্তব উদাহরণ: একজন ডেভেলপার একসাথে দুইটা ভিন্ন ফিচারের কোড লিখে ফেলেছেন একই সেশনে; কমিট আলাদা রাখতে শুধু লগিন-সম্পর্কিত ফাইলগুলো git add login.js auth.css দিয়ে স্টেজ করে প্রথমে সেটার কমিট করেন।

১২. মডিফাই হওয়া সব ফাইল একসাথে স্টেজ করা

সিনারিও (কী ঘটেছে): মডিফাই হওয়া সব ফাইল একসাথে রেডি (Stage) করতে চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: git add . বর্তমান ডিরেক্টরি ও তার সাবফোল্ডারে থাকা সব modified ও নতুন (untracked, কিন্তু .gitignore-এ নেই এমন) ফাইলের জন্য blob বানিয়ে index আপডেট করে।

সমাধান:

git add .
git add -A   # ডিলিট হওয়া ফাইলসহ পুরো রিপোজিটরির সব পরিবর্তন স্টেজ করে

বাস্তব উদাহরণ: একটা দিনের কাজ শেষে একজন ডেভেলপার নিশ্চিত হয়ে যান সব পরিবর্তনই এক ফিচারের অংশ, তাই আলাদা করে ফাইল না বেছে সরাসরি git add . দিয়ে সব স্টেজ করে একটা কমিট দেন।

১৩. মেসেজসহ কমিট করা

সিনারিও (কী ঘটেছে): কোডের পরিবর্তনগুলো একটি মেসেজ দিয়ে সেভ করতে চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: git commit index-এ থাকা স্ন্যাপশট থেকে একটা tree object আর একটা commit object তৈরি করে; commit object-এ মেসেজ, author/committer তথ্য, timestamp আর parent commit-এর হ্যাশ থাকে। শেষে বর্তমান ব্রাঞ্চের ref নতুন commit-এ সরে যায়।

সমাধান:

git commit -m "Added login feature"

বাস্তব উদাহরণ: একজন ডেভেলপার লগিন ফিচার শেষ করে স্পষ্ট মেসেজ git commit -m "Added login feature" দিয়ে কমিট করেন, যাতে ৬ মাস পরে git log দেখলে যে কেউ বুঝে যায় ওই কমিটে কী যোগ হয়েছিল।

১৪. add ও commit একসাথে করা

সিনারিও (কী ঘটেছে): ইতিমধ্যে ট্র্যাক করা ফাইলের জন্য add এবং commit একসাথে এক কমান্ডে করতে চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: -a ফ্ল্যাগ শুধুমাত্র ইতিমধ্যে ট্র্যাক করা (আগে অন্তত একবার add হওয়া) ফাইলগুলোকে অটো-স্টেজ করে তারপর কমিট করে — নতুন (untracked) ফাইল এতে যুক্ত হয় না।

সমাধান:

git commit -am "Updated text"

বাস্তব উদাহরণ: একজন ডেভেলপার শুধু একটা টাইপো ঠিক করেছেন একটা আগে থেকে ট্র্যাক করা ফাইলে; আলাদা করে add না চালিয়ে সরাসরি git commit -am "Fix typo" দিয়ে এক কমান্ডে কাজ সারেন।

১৫. সর্বশেষ কমিট মেসেজ ঠিক করা

সিনারিও (কী ঘটেছে): সর্বশেষ কমিটের মেসেজ ভুল হয়েছে, ঠিক করতে চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: --amend পুরনো commit-কে এডিট করে না — বরং নতুন মেসেজসহ একটা নতুন commit object বানিয়ে বর্তমান ব্রাঞ্চের ref-কে সেই নতুন commit-এ সরিয়ে দেয়; পুরনো commit hash-টা “orphan” হয়ে যায় (reflog-এ কিছুদিন থেকে যায়)।

সমাধান:

git commit --amend -m "Corrected message"

বাস্তব উদাহরণ: একজন ডেভেলপার কমিট করার পরপরই লক্ষ্য করেন মেসেজে বানান ভুল (“Fxied bug” লিখেছিলেন); push করার আগেই --amend দিয়ে ঠিক করে নেন। সতর্কতা: কমিটটা যদি ইতিমধ্যে push করা হয়ে থাকে এবং অন্যরা সেটার উপর কাজ করে থাকে, তাহলে amend করলে hash বদলে যাবে এবং পরে force push লাগবে — টিমকে জানিয়ে করুন।

১৬. ভুলে বাদ পড়া ফাইল আগের কমিটে যোগ করা

সিনারিও (কী ঘটেছে): কমিট করার পর মনে পড়ল একটি ফাইল add করতে ভুলে গেছেন, কিন্তু নতুন আলাদা কমিট বানাতে চান না।

কেন এটা হয় / ইন্টারনাল মেকানিজম: নতুন ফাইলটা স্টেজ করার পর --amend --no-edit আগের commit-এর মেসেজ অপরিবর্তিত রেখে ওই commit-এর tree-তে নতুন ফাইলটা যোগ করে একটা নতুন commit object বানায়, ref সেখানে সরে যায়।

সমাধান:

git add forgotten-file.js
git commit --amend --no-edit

বাস্তব উদাহরণ: একজন ডেভেলপার CSS ফাইল কমিট করার পর বুঝতে পারেন সাথে একটা ইমেজ অ্যাসেটও যোগ করা দরকার ছিল; ফাইলটা add করে --amend --no-edit দিয়ে একই কমিটে জুড়ে দেন, ফলে হিস্ট্রিতে দুইটা আলাদা অসম্পূর্ণ কমিট থাকে না।

১৭. স্টেজ করা ফাইল আনস্টেজ করা

সিনারিও (কী ঘটেছে): কোনো ফাইল স্টেজ (add) করে ফেলেছেন, কিন্তু এখন আনস্টেজ করতে চান — ফাইলের পরিবর্তন রেখে দিয়ে।

কেন এটা হয় / ইন্টারনাল মেকানিজম: git restore --staged ইনডেক্স-এ ওই ফাইলের এন্ট্রিকে আবার HEAD commit-এর ভার্সনে ফিরিয়ে দেয় — working directory-র ফাইল স্পর্শ করা হয় না, তাই আপনার পরিবর্তন ফাইলে থেকেই যায়, শুধু staging list থেকে বাদ পড়ে।

সমাধান:

git restore --staged <file-name>
# পুরনো সিনট্যাক্স (এখনো কাজ করে):
git reset <file-name>

বাস্তব উদাহরণ: একজন ডেভেলপার git add . চালিয়ে ভুলে একটা ডিবাগ-লগ ফাইলও স্টেজ করে ফেলেন; কমিট করার আগে git restore --staged debug.log দিয়ে শুধু ওই ফাইলটা আনস্টেজ করে বাকিগুলো কমিট করেন।

১৮. পরিবর্তন বাতিল করে আগের অবস্থায় ফেরা

সিনারিও (কী ঘটেছে): কোডে অনেক পরিবর্তন করেছেন, কিন্তু সব বাতিল করে আগের অবস্থায় (সর্বশেষ কমিট করা অবস্থায়) ফিরে যেতে চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: git restore <file> working directory-তে থাকা ফাইলটাকে HEAD (বা --staged-এর ক্ষেত্রে index) commit-এর ভার্সন দিয়ে ওভাররাইট করে দেয়।

সমাধান:

git restore <file-name>

বাস্তব উদাহরণ: একজন ডেভেলপার একটা এক্সপেরিমেন্টাল রিফ্যাক্টর শুরু করেছিলেন যা কাজ করছে না; git restore utils.py চালিয়ে ফাইলটা শেষ কমিটের অবস্থায় ফিরিয়ে নেন এবং নতুন করে শুরু করেন। সতর্কতা: এটি uncommitted পরিবর্তন স্থায়ীভাবে মুছে দেয় — কোনো stash বা reflog দিয়ে ফেরত পাওয়া যায় না, তাই চালানোর আগে নিশ্চিত হন।

১৯. কমিট করার আগে diff দেখা

সিনারিও (কী ঘটেছে): কমিট করার আগে দেখতে চান কোডে ঠিক কী কী লাইন পরিবর্তন হয়েছে।

কেন এটা হয় / ইন্টারনাল মেকানিজম: git diff (আর্গুমেন্ট ছাড়া) working directory-র ফাইল আর index-এ থাকা ভার্সনের মধ্যে লাইন-বাই-লাইন তুলনা করে দেখায় — এখনো স্টেজ না করা পরিবর্তনগুলো।

সমাধান:

git diff

বাস্তব উদাহরণ: কমিট করার আগে একজন সিনিয়র ডেভেলপার অভ্যাসগতভাবে git diff চালিয়ে নিজের করা পরিবর্তন রিভিউ করেন, যাতে ভুলে কোনো console.log বা টেস্ট কোড রয়ে না যায়।

২০. স্টেজ করা ফাইলের diff দেখা

সিনারিও (কী ঘটেছে): স্টেজ (add) করা ফাইলগুলোর পরিবর্তন দেখতে চান — কমিট করার ঠিক আগে শেষ যাচাই হিসেবে।

কেন এটা হয় / ইন্টারনাল মেকানিজম: git diff --staged (বা --cached) index-এ থাকা ভার্সন আর HEAD commit-এর ভার্সনের মধ্যে তুলনা করে দেখায় — অর্থাৎ পরবর্তী কমিটে ঠিক কী কী যাবে তার প্রিভিউ।

সমাধান:

git diff --staged

বাস্তব উদাহরণ: একজন ডেভেলপার git add . করার পর কমিট করার আগে git diff --staged দিয়ে নিশ্চিত হন ঠিক কোন কোন লাইন কমিট হতে যাচ্ছে, বিশেষ করে একাধিক ফাইলে ছোট ছোট পরিবর্তন করার পর।

🌿 ক্যাটাগরি ৩: ব্রাঞ্চিং (Branching) (২১-৩০)

২১. সব ব্রাঞ্চের লিস্ট দেখা

সিনারিও (কী ঘটেছে): প্রজেক্টে কী কী ব্রাঞ্চ আছে দেখতে চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: git branch .git/refs/heads/ ডিরেক্টরিতে থাকা প্রতিটা ফাইলকে (প্রতিটাই একটা ব্রাঞ্চ) লিস্ট করে, বর্তমান ব্রাঞ্চের পাশে * চিহ্ন দেখায়।

সমাধান:

git branch

বাস্তব উদাহরণ: কয়েক সপ্তাহ ধরে কাজ করা একজন ডেভেলপার ভুলে গেছেন কতগুলো ফিচার-ব্রাঞ্চ খোলা রেখেছেন; git branch চালিয়ে পুরনো, আর দরকার নেই এমন ব্রাঞ্চগুলো চিহ্নিত করেন।

২২. নতুন ব্রাঞ্চ তৈরি করা

সিনারিও (কী ঘটেছে): নতুন একটি ব্রাঞ্চ তৈরি করতে চান, কিন্তু এখনই সেখানে move করতে চান না।

কেন এটা হয় / ইন্টারনাল মেকানিজম: refs/heads/feature-x নামে একটা নতুন ছোট ফাইল বানায় যেটা বর্তমান HEAD commit-এর হ্যাশ ধরে রাখে — কোনো ফাইল কপি হয় না, তাই এটা প্রায় বিনামূল্যে ও ইনস্ট্যান্ট অপারেশন।

সমাধান:

git branch feature-x

বাস্তব উদাহরণ: একজন টিম লিড স্প্রিন্ট প্ল্যানিং করার সময় আগে থেকেই টিমের জন্য কয়েকটা ফিচার-ব্রাঞ্চ (feature-x, feature-y) বানিয়ে রাখেন, যাতে ডেভেলপাররা কাজ শুরু করার সাথে সাথে switch করে নিতে পারেন।

২৩. অন্য ব্রাঞ্চে সুইচ করা

সিনারিও (কী ঘটেছে): অন্য একটি ব্রাঞ্চে সুইচ করে যেতে চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: HEAD ফাইলকে নতুন ব্রাঞ্চ রেফারেন্সে পয়েন্ট করানো হয়, এবং working directory-র ফাইলগুলো সেই ব্রাঞ্চের সর্বশেষ commit-এর স্ন্যাপশট দিয়ে রিপ্লেস করা হয়।

সমাধান:

git switch feature-x
# অথবা পুরনো কমান্ড:
git checkout feature-x

বাস্তব উদাহরণ: কোড রিভিউ করার জন্য একজন সিনিয়র ডেভেলপার সহকর্মীর ব্রাঞ্চে git switch feature-x দিয়ে সুইচ করে লোকালি চালিয়ে দেখেন ফিচারটা ঠিকমতো কাজ করছে কিনা।

২৪. এক কমান্ডে ব্রাঞ্চ তৈরি ও সুইচ করা

সিনারিও (কী ঘটেছে): এক কমান্ডে নতুন ব্রাঞ্চ তৈরি করে সেটিতে চলে যেতে চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: এটি “ব্রাঞ্চ তৈরি” আর “সুইচ” — দুটো ধাপ একসাথে একটা কমান্ডে সম্পন্ন করে।

সমাধান:

git switch -c feature-y
# অথবা পুরনো কমান্ড:
git checkout -b feature-y

বাস্তব উদাহরণ: স্ট্যান্ডআপ মিটিং শেষে নতুন টাস্ক পাওয়ার সাথে সাথে একজন ডেভেলপার সরাসরি git switch -c feature-payment-retry দিয়ে কাজ শুরু করে দেন, আলাদা করে branch তৈরির ধাপ বাদ দিয়ে।

২৫. ব্রাঞ্চের নাম পরিবর্তন করা

সিনারিও (কী ঘটেছে): বর্তমান ব্রাঞ্চের নাম পরিবর্তন করতে চান — হয়তো টাইপো ছিল বা নাম-কনভেনশন বদলেছে।

কেন এটা হয় / ইন্টারনাল মেকানিজম: refs/heads/ ডিরেক্টরিতে থাকা ফাইলের নাম বদলে দেয় (একই commit hash-কে পয়েন্ট করে); রিমোটে আগে থেকে push করা থাকলে রিমোট ব্রাঞ্চ আলাদাভাবে আপডেট করতে হয়।

সমাধান:

git branch -m new-branch-name
# রিমোটেও রিফ্লেক্ট করতে:
git push origin -u new-branch-name
git push origin --delete old-branch-name

বাস্তব উদাহরণ: একজন ডেভেলপার তাড়াহুড়ায় ব্রাঞ্চের নাম feture-login (বানান ভুল) দিয়ে ফেলেছিলেন; পুশ করার আগেই git branch -m feature-login দিয়ে ঠিক করে নেন।

২৬. মার্জ হওয়া ব্রাঞ্চ ডিলিট করা

সিনারিও (কী ঘটেছে): একটি ব্রাঞ্চ মুছে ফেলতে চান, যার কাজ ইতিমধ্যে main-এ মার্জ হয়ে গেছে।

কেন এটা হয় / ইন্টারনাল মেকানিজম: -d ফ্ল্যাগ শুধু refs/heads/-এর ফাইলটা ডিলিট করে দেয় — এটা নিরাপত্তা-চেক করে যে ব্রাঞ্চের কমিটগুলো অন্য কোনো ব্রাঞ্চে (সাধারণত বর্তমান ব্রাঞ্চে) reachable কিনা; না হলে ডিলিট করতে দেয় না।

সমাধান:

git branch -d feature-x

বাস্তব উদাহরণ: একটা ফিচার মার্জ হয়ে প্রোডাকশনে যাওয়ার পর টিম লিড নিয়মিত পুরনো লোকাল ব্রাঞ্চগুলো git branch -d দিয়ে পরিষ্কার করেন, যাতে git branch লিস্ট এলোমেলো না হয়ে যায়।

২৭. জোরপূর্বক ব্রাঞ্চ ডিলিট করা

সিনারিও (কী ঘটেছে): ব্রাঞ্চটি এখনও মার্জ করা হয়নি, তবুও জোরপূর্বক মুছে ফেলতে চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: -D আসলে --delete --force-এর শর্টকাট — এটি reachability-চেক এড়িয়ে সরাসরি ref ফাইল ডিলিট করে দেয়, তাই ওই ব্রাঞ্চের একমাত্র-নিজস্ব কমিটগুলো কোনো ব্রাঞ্চ থেকেই আর reachable থাকে না।

সমাধান:

git branch -D feature-x

বাস্তব উদাহরণ: একজন ডেভেলপার একটা এক্সপেরিমেন্টাল ব্রাঞ্চ বানিয়ে দেখেন আইডিয়াটা কাজ করছে না; সেটা মার্জ না করেই git branch -D experiment-x দিয়ে বাতিল করে দেন। সতর্কতা: ব্রাঞ্চের কমিট অন্য কোথাও reachable না থাকলে এটা কার্যত ওই কাজ হারিয়ে ফেলা — ডিলিট করার আগে নিশ্চিত হন সত্যিই আর দরকার নেই (git reflog দিয়ে কিছুদিন রিকভারি সম্ভব, কিন্তু নির্ভরযোগ্য নয়)।

২৮. রিমোটসহ সব ব্রাঞ্চ দেখা

সিনারিও (কী ঘটেছে): গিটহাবের (রিমোট) সব ব্রাঞ্চসহ লোকাল ব্রাঞ্চ একসাথে দেখতে চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: -a লোকাল refs/heads/ আর সর্বশেষ fetch করা refs/remotes/origin/ — দুটো লিস্টই একসাথে দেখায়; মনে রাখা জরুরি remote-tracking branch গুলো শুধু লোকাল ক্যাশ, আসল রিমোটের বর্তমান অবস্থা git fetch না করলে আপডেট হয় না।

সমাধান:

git branch -a

বাস্তব উদাহরণ: একজন ডেভেলপার নতুন টিমে জয়েন করে বুঝতে চান টিমে কতগুলো active ফিচার-ব্রাঞ্চ চলছে; git branch -a দিয়ে রিমোটের সব ব্রাঞ্চের তালিকা দেখে নেন।

২৯. রিমোট ব্রাঞ্চ লোকালে নামানো

সিনারিও (কী ঘটেছে): রিমোটের একটি নির্দিষ্ট ব্রাঞ্চ লোকাল পিসিতে নামিয়ে সেখানে কাজ করতে চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: এটি origin/<branch> remote-tracking ref থেকে একটা নতুন লোকাল ব্রাঞ্চ বানায় এবং সেটাকে সেই remote branch-এর সাথে “ট্র্যাকিং” সম্পর্কে যুক্ত করে দেয় (upstream সেট হয়), ফলে পরে শুধু git pull/git push লিখলেই চলবে।

সমাধান:

git checkout -b <branch> origin/<branch>
# অথবা আধুনিক শর্টকাট (origin/<branch> নাম মিললে):
git switch <branch>

বাস্তব উদাহরণ: একজন ডেভেলপার সহকর্মীর তৈরি করা feature-search ব্রাঞ্চে কাজ করতে বলা হয়েছে; git checkout -b feature-search origin/feature-search চালিয়ে লোকালি সেই ব্রাঞ্চ নামিয়ে কাজ শুরু করেন।

৩০. মার্জ না হওয়া ব্রাঞ্চ খুঁজে বের করা

সিনারিও (কী ঘটেছে): কোন ব্রাঞ্চগুলো এখনো main-এর সাথে মার্জ করা হয়নি তা দেখতে চান — পরিষ্কার করার আগে যাচাই হিসেবে।

কেন এটা হয় / ইন্টারনাল মেকানিজম: এটি প্রতিটা ব্রাঞ্চের commit বর্তমান HEAD থেকে reachable কিনা তা চেক করে; reachable না হলে (অর্থাৎ ওই ব্রাঞ্চের কাজ এখনো main-এ আসেনি) সেটাকে “no-merged” হিসেবে দেখায়।

সমাধান:

git branch --no-merged
git branch --no-merged main   # নির্দিষ্ট ব্রাঞ্চের সাপেক্ষে চেক করতে

বাস্তব উদাহরণ: পুরনো ব্রাঞ্চ পরিষ্কার করার আগে একজন টিম লিড git branch --no-merged main চালিয়ে দেখেন কোন ব্রাঞ্চগুলো এখনো সক্রিয় কাজ ধরে আছে, যাতে ভুল করে দরকারি কাজ ডিলিট না হয়ে যায়।

🔀 ক্যাটাগরি ৪: মার্জিং এবং সিঙ্কিং (৩১-৪০)

৩১. ব্রাঞ্চ মার্জ করা

সিনারিও (কী ঘটেছে): feature-x ব্রাঞ্চের কাজ শেষ, সেটিকে বর্তমান ব্রাঞ্চের (যেমন main) সাথে মেলাতে (Merge) চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: গিট দুই ব্রাঞ্চের কমন পূর্বপুরুষ (merge base) বের করে। যদি main-এ merge base-এর পর নতুন কোনো কমিট না থাকে, তাহলে শুধু ref সরিয়ে দেয় (fast-forward)। নাহলে দুই দিকের পরিবর্তন combine করে দুইটা parent-সহ একটা নতুন merge commit বানায়; একই লাইনে দুই দিকে ভিন্ন পরিবর্তন থাকলে conflict দেখায়।

সমাধান:

git switch main
git merge feature-x

বাস্তব উদাহরণ: একটা ই-কমার্স প্রজেক্টে “কার্ট” ফিচারের কাজ শেষ হওয়ার পর ডেভেলপার main ব্রাঞ্চে ফিরে git merge feature-cart চালিয়ে কাজটা মূল কোডে যুক্ত করেন।

৩২. রিমোট থেকে পুল করা

সিনারিও (কী ঘটেছে): গিটহাব থেকে প্রজেক্টের লেটেস্ট কোড লোকাল পিসিতে নামাতে চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: এটি fetch (রিমোটের নতুন commit ও ref লোকালে নামানো) আর merge (origin/main-কে বর্তমান ব্রাঞ্চের সাথে মেলানো) — দুটো ধাপের সমষ্টি।

সমাধান:

git pull origin main

বাস্তব উদাহরণ: সকালে কাজ শুরুর আগে একজন ডেভেলপার অভ্যাসমতো git pull origin main চালিয়ে রাতারাতি টিমের বাকিদের করা পরিবর্তন নিজের লোকাল কোডে নিয়ে আসেন, যাতে পুরনো কোডের উপর কাজ শুরু না হয়।

৩৩. রিমোটে পুশ করা

সিনারিও (কী ঘটেছে): আপনার লোকাল কোড গিটহাবে আপলোড করতে চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: লোকাল ব্রাঞ্চে থাকা যেসব commit রিমোট ব্রাঞ্চে এখনো নেই, সেগুলো আর তাদের object (blob/tree/commit) রিমোট সার্ভারে পাঠিয়ে রিমোট ব্রাঞ্চের ref আপডেট করা হয়। রিমোট ref আপনার লোকাল হিস্ট্রির “ancestor” না হলে গিট push প্রত্যাখ্যান করে (নন-fast-forward)।

সমাধান:

git push origin main

বাস্তব উদাহরণ: ফিচার শেষে টেস্ট পাশ করার পর একজন ডেভেলপার git push origin main চালিয়ে কোড আপলোড করেন, যাতে CI/CD পাইপলাইন চালু হয়ে ডিপ্লয়মেন্ট শুরু হয়।

৩৪. নতুন ব্রাঞ্চ প্রথমবার পুশ করা

সিনারিও (কী ঘটেছে): নতুন ব্রাঞ্চ গিটহাবে প্রথমবারের মতো আপলোড করতে চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: -u (--set-upstream) ব্রাঞ্চটা প্রথমবার রিমোটে তৈরি করার পাশাপাশি লোকাল ব্রাঞ্চের সাথে সেই remote branch-এর একটা ট্র্যাকিং সম্পর্ক সেট করে দেয় — পরের বার থেকে শুধু git push/git pull লিখলেই যথেষ্ট হয়।

সমাধান:

git push -u origin <branch-name>

বাস্তব উদাহরণ: একজন ডেভেলপার নতুন feature-notifications ব্রাঞ্চে কাজ শেষে প্রথমবার git push -u origin feature-notifications চালান, যাতে GitHub-এ একটা Pull Request খোলার অপশন চলে আসে।

৩৫. কনফ্লিক্টের মার্জ বাতিল করা

সিনারিও (কী ঘটেছে): মার্জ করার সময় কনফ্লিক্ট (Conflict) হলো, আপনি মার্জ পুরোপুরি বাতিল করতে চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: merge --abort in-progress মার্জ স্টেট (যেসব ফাইলে conflict marker বসানো হয়েছিল) মুছে working directory ও index-কে merge শুরু হওয়ার ঠিক আগের অবস্থায় ফিরিয়ে দেয়।

সমাধান:

git merge --abort

বাস্তব উদাহরণ: একজন ডেভেলপার মার্জ শুরু করার পর দেখেন ১৫টার বেশি ফাইলে কনফ্লিক্ট এসেছে এবং বুঝতে পারেন ভুল ব্রাঞ্চ মার্জ করেছেন; git merge --abort দিয়ে সব বাতিল করে সঠিক ব্রাঞ্চ দিয়ে আবার শুরু করেন।

৩৬. রিমোটের পরিবর্তন fetch করা

সিনারিও (কী ঘটেছে): রিমোটে (গিটহাবে) কী কী নতুন পরিবর্তন এসেছে তা চেক করতে চান, কিন্তু নিজের কোড এখনই পরিবর্তন করতে চান না।

কেন এটা হয় / ইন্টারনাল মেকানিজম: fetch শুধু রিমোটের নতুন commit ও object ডাউনলোড করে refs/remotes/origin/*-এ আপডেট করে — বর্তমান ব্রাঞ্চ বা working directory স্পর্শ করে না, তাই এটা সম্পূর্ণ নিরাপদ একটা “দেখে নেওয়া” অপারেশন।

সমাধান:

git fetch
git log HEAD..origin/main --oneline   # কী কী নতুন এসেছে দেখতে

বাস্তব উদাহরণ: একজন ডেভেলপার নিজের অসমাপ্ত কাজের মাঝে জানতে চান টিমের বাকিরা কী পরিবর্তন করেছে; git fetch চালিয়ে নিরাপদে দেখে নেন, নিজের কোড না ছুঁয়েই।

৩৭. ডিলিট হওয়া রিমোট ব্রাঞ্চ prune করা

সিনারিও (কী ঘটেছে): রিমোটে কোনো ব্রাঞ্চ ডিলিট করা হয়েছে, আপনার লোকাল পিসির remote-tracking ব্রাঞ্চ লিস্ট থেকেও তা আপডেট করতে চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: fetch স্বাভাবিকভাবে শুধু নতুন ref যোগ করে, রিমোটে ডিলিট হওয়া ব্রাঞ্চের স্থানীয় “ছায়া” (refs/remotes/origin/<branch>) নিজে থেকে সরায় না। -p (prune) সেই stale remote-tracking ref গুলো খুঁজে ডিলিট করে দেয়।

সমাধান:

git fetch -p

বাস্তব উদাহরণ: একজন ডেভেলপার git branch -a-তে অনেক পুরনো ব্রাঞ্চ দেখতে পান যা আসলে GitHub-এ PR মার্জের পর ডিলিট হয়ে গেছে; git fetch -p চালিয়ে লোকাল ভিউ পরিষ্কার করেন।

৩৮. রিমোট ব্রাঞ্চ ডিলিট করা

সিনারিও (কী ঘটেছে): গিটহাব থেকে (রিমোটে) সরাসরি একটি ব্রাঞ্চ ডিলিট করতে চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: এটি রিমোট সার্ভারকে নির্দেশ পাঠায় তার refs/heads/<branch> ফাইলটা মুছে ফেলতে — এটা push অপারেশনেরই একটা রূপ (একটা ref-কে “কোনো কমিটে নয়” এভাবে আপডেট করা)।

সমাধান:

git push origin --delete <branch-name>

বাস্তব উদাহরণ: PR মার্জ হওয়ার পর একজন ডেভেলপার git push origin --delete feature-cart চালিয়ে রিমোট থেকে ব্রাঞ্চটা সরিয়ে ফেলেন, টিমের ব্রাঞ্চ লিস্ট পরিষ্কার রাখতে।

৩৯. অন্য ব্রাঞ্চ থেকে একটা ফাইল আনা

সিনারিও (কী ঘটেছে): শুধু একটি নির্দিষ্ট ফাইল অন্য ব্রাঞ্চ থেকে বর্তমান ব্রাঞ্চে কপি করে আনতে চান, পুরো ব্রাঞ্চ merge না করে।

কেন এটা হয় / ইন্টারনাল মেকানিজম: -- এর পরের অংশ পাথ হিসেবে ধরে গিট শুধু সেই নির্দিষ্ট ফাইলের ভার্সনটা অন্য ব্রাঞ্চ থেকে নিয়ে এসে working directory ও index-এ বসিয়ে দেয় — বাকি ফাইল বা ব্রাঞ্চ পয়েন্টারে কোনো পরিবর্তন হয় না।

সমাধান:

git checkout <branch-name> -- <file-name>
# আধুনিক বিকল্প:
git restore --source=<branch-name> -- <file-name>

বাস্তব উদাহরণ: একজন ডেভেলপার একটা আলাদা experiment ব্রাঞ্চে একটা util ফাইলে ভালো একটা ফিক্স করেছিলেন; পুরো ব্রাঞ্চ merge না করে শুধু git checkout experiment -- utils.js দিয়ে ওই একটা ফাইল main-এ নিয়ে আসেন।

৪০. no-fast-forward মার্জ করা

সিনারিও (কী ঘটেছে): মার্জ করার সময় ফাস্ট-ফরোয়ার্ড (Fast-forward) না করে, ইতিহাসে স্পষ্ট একটা মার্জ কমিট মেসেজ রাখতে চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: --no-ff ফাস্ট-ফরোয়ার্ড সম্ভব হলেও জোর করে একটা আসল merge commit (দুই parent-সহ) বানায় — এতে হিস্ট্রিতে স্পষ্ট দেখা যায় কখন কোন ফিচার-ব্রাঞ্চ merge হয়েছিল, লিনিয়ার হিস্ট্রিতে যা হারিয়ে যেত।

সমাধান:

git merge --no-ff <branch-name>

বাস্তব উদাহরণ: একটা টিম রিলিজ হিস্ট্রি ট্র্যাক করার জন্য সবসময় --no-ff দিয়ে ফিচার-ব্রাঞ্চ merge করার নিয়ম রাখে, যাতে git log --graph দেখলে প্রতিটা ফিচার কবে merge হয়েছিল তা স্পষ্ট বোঝা যায়।

⏪ ক্যাটাগরি ৫: ভুল সংশোধন ও আন্ডু (Undo) করা (৪১-৫০)

৪১. soft reset দিয়ে কমিট আনডু করা

সিনারিও (কী ঘটেছে): সর্বশেষ কমিটটি মুছে ফেলতে চান, কিন্তু কোডের পরিবর্তনগুলো রেখে দিতে চান (যাতে আবার কমিট করা যায়)।

কেন এটা হয় / ইন্টারনাল মেকানিজম: --soft শুধু বর্তমান ব্রাঞ্চের ref-কে টার্গেট commit-এ সরিয়ে দেয় — index আর working directory অপরিবর্তিত থাকে। ফলে আগের কমিটের সব পরিবর্তন এখন আবার “staged” অবস্থায় দেখা যায়, নতুন করে কমিট করার জন্য প্রস্তুত।

সমাধান:

git reset --soft HEAD~1

বাস্তব উদাহরণ: একজন ডেভেলপার তাড়াহুড়ায় একটা অসম্পূর্ণ ফিচার কমিট করে ফেলেছিলেন; git reset --soft HEAD~1 দিয়ে কমিটটা তুলে নিয়ে বাকি কাজ শেষ করে আরও ভালো মেসেজসহ নতুন করে কমিট করেন।

৪২. hard reset দিয়ে কমিট ও পরিবর্তন মুছা

সিনারিও (কী ঘটেছে): সর্বশেষ কমিট এবং কোডের পরিবর্তন—সবকিছু পুরোপুরি মুছে ফেলতে চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: --hard ব্রাঞ্চ ref, index, আর working directory — তিনটাই টার্গেট commit-এর অবস্থায় ওভাররাইট করে দেয়।

সমাধান:

git reset --hard HEAD~1

বাস্তব উদাহরণ: একজন ডেভেলপার একটা এক্সপেরিমেন্ট শুরু করে বুঝতে পারেন পুরো আইডিয়াটাই ভুল দিকে যাচ্ছে; git reset --hard HEAD~1 দিয়ে সর্বশেষ কমিট আর তার সব পরিবর্তন একবারে মুছে আগের অবস্থায় ফিরে যান। সতর্কতা: এটি destructive — uncommitted পরিবর্তন ও নির্দিষ্ট করা কমিট স্থায়ীভাবে হারিয়ে যায় (reflog দিয়ে সাময়িক রিকভারি সম্ভব, কিন্তু নিশ্চিত না)। শেয়ার্ড/পুশ করা ব্রাঞ্চে চালানোর আগে দুইবার ভাবুন।

৪৩. আগের নির্দিষ্ট কমিটে ফিরে যাওয়া

সিনারিও (কী ঘটেছে): আগের একটি নির্দিষ্ট কমিটের অবস্থায় পুরো প্রজেক্ট ফিরিয়ে নিতে চান (মাঝখানের সব কমিট বাদ দিয়ে)।

কেন এটা হয় / ইন্টারনাল মেকানিজম: ঠিক আগের আইটেমের মতোই, শুধু টার্গেট এখানে HEAD~N-এর বদলে একটা নির্দিষ্ট commit hash — ব্রাঞ্চ ref সেই commit-এ সরে যায়, মাঝের commit গুলো ব্রাঞ্চ থেকে unreachable হয়ে যায়।

সমাধান:

git reset --hard <commit-hash>

বাস্তব উদাহরণ: একটা রিলিজের ঠিক আগের একটা স্টেবল কমিটে ফিরে গিয়ে নতুন করে কাজ শুরু করার জন্য একজন ডেভেলপার git log --oneline থেকে হ্যাশ কপি করে git reset --hard <hash> চালান। সতর্কতা: মাঝখানের সব কমিট (ও তাদের পরিবর্তন) ব্রাঞ্চ থেকে বাদ পড়ে যায় — নিশ্চিত হয়ে নিন এই কমিটগুলো সত্যিই আর দরকার নেই, বা আগে একটা ব্যাকআপ ব্রাঞ্চ বানিয়ে রাখুন।

৪৪. revert দিয়ে নিরাপদে আনডু করা

সিনারিও (কী ঘটেছে): আগের একটি কমিটের পরিবর্তন বাতিল করতে চান, কিন্তু হিস্ট্রি না মুছে নতুন একটি ‘Undo’ কমিট তৈরি করে (সবচেয়ে নিরাপদ)।

কেন এটা হয় / ইন্টারনাল মেকানিজম: টার্গেট commit-এর diff-কে উল্টো দিকে (inverse) apply করে একটা সম্পূর্ণ নতুন commit তৈরি করে — মূল commit history-তে অক্ষত থেকে যায়, শুধু নতুন একটা commit যোগ হয় যা আগের পরিবর্তন বাতিল করে।

সমাধান:

git revert <commit-hash>
git revert --no-commit <hash1> <hash2>   # একাধিক commit একসাথে revert করে একটা কমিটে বাঁধতে

বাস্তব উদাহরণ: প্রোডাকশনে ডিপ্লয় হওয়ার পর একটা কমিটে বাগ ধরা পড়ে; যেহেতু main ব্রাঞ্চ ইতিমধ্যে টিমের সবাই pull করে ফেলেছে, reset ব্যবহার না করে git revert <hash> দিয়ে নিরাপদে বাগটা বাতিল করা হয়, যাতে কারো লোকাল হিস্ট্রি নষ্ট না হয়।

৪৫. ভুলে main-এ কাজ শুরু করা সরিয়ে ফেলা

সিনারিও (কী ঘটেছে): ভুলে মূল ব্রাঞ্চে (main) কাজ শুরু করেছেন, এখনো কমিট করেননি। কাজগুলো নতুন ব্রাঞ্চে সরাতে চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: যেহেতু কোনো কমিট হয়নি, পরিবর্তনগুলো শুধু working directory ও index-এ আছে — নতুন ব্রাঞ্চ তৈরি করলে HEAD নতুন ব্রাঞ্চে সরে যায় কিন্তু working directory/index অপরিবর্তিত থাকে, তাই uncommitted পরিবর্তন স্বয়ংক্রিয়ভাবে নতুন ব্রাঞ্চে “চলে আসে”।

সমাধান:

git switch -c new-branch

বাস্তব উদাহরণ: একজন ডেভেলপার তাড়াহুড়ায় সরাসরি main-এ কোড লিখতে শুরু করে দেন, পরে খেয়াল হয় এটা একটা ফিচার-ব্রাঞ্চে হওয়া উচিত ছিল; কমিট করার আগেই git switch -c feature-export চালিয়ে নিরাপদে কাজটা সরিয়ে নেন।

৪৬. ভুলে main-এ কমিট হয়ে যাওয়া ব্রাঞ্চে সরানো

সিনারিও (কী ঘটেছে): ভুলে main ব্রাঞ্চে কমিট করে ফেলেছেন। কমিটটি অন্য (নতুন) ব্রাঞ্চে নিতে চান, main-কে আগের অবস্থায় রেখে।

কেন এটা হয় / ইন্টারনাল মেকানিজম: প্রথমে git branch new-branch বর্তমান commit-কে পয়েন্ট করা একটা নতুন ব্রাঞ্চ বানায় (তাই commit-টা এখন দুই ব্রাঞ্চ থেকেই reachable)। তারপর main-এ reset --hard HEAD~1 main-এর ref-কে এক ধাপ পেছনে সরায় (commit-টা তখনও new-branch থেকে reachable বলে হারায় না)। শেষে সুইচ করে নতুন ব্রাঞ্চে চলে যাওয়া হয়।

সমাধান:

git branch new-branch
git reset --hard HEAD~1
git switch new-branch

বাস্তব উদাহরণ: একজন ডেভেলপার তাড়াহুড়ায় একটা হটফিক্স সরাসরি main-এ কমিট করে ফেলেন, অথচ টিমের নিয়ম হলো সব পরিবর্তন PR-এর মাধ্যমে আসা; ঠিক এই তিন-ধাপের কমান্ড চালিয়ে কমিটটা hotfix-payment ব্রাঞ্চে সরিয়ে main-কে আগের ক্লিন অবস্থায় ফেরত আনেন।

৪৭. ট্র্যাক করা sensitive ফাইল আনট্র্যাক করা

সিনারিও (কী ঘটেছে): এমন একটি ফাইল আনট্র্যাক (Untrack) করতে চান যা গিট ইতোমধ্যে ট্র্যাক করছে (যেমন .env), কিন্তু পিসি থেকে মুছতে চান না।

কেন এটা হয় / ইন্টারনাল মেকানিজম: --cached ফাইলটাকে শুধু index থেকে সরায় (তাই পরের কমিট থেকে গিট আর এটা ট্র্যাক করবে না) — working directory-র আসল ফাইল অক্ষত থাকে। .gitignore-এ যোগ করার সাথে এটা মিলিয়ে করলে ফাইলটা ভবিষ্যতেও আর ট্র্যাক হবে না।

সমাধান:

git rm --cached .env
echo ".env" >> .gitignore
git commit -m "Untrack .env file"

বাস্তব উদাহরণ: একটা প্রজেক্টের প্রথম দিকে .env ফাইল ভুলে কমিট হয়ে গিয়েছিল; একজন ডেভেলপার git rm --cached .env দিয়ে ট্র্যাকিং বন্ধ করে .gitignore-এ যোগ করে দেন। সতর্কতা: ফাইলটা যদি আগেই push হয়ে থাকে, তাহলে পুরনো কমিটে এখনও সেটার কনটেন্ট (পাসওয়ার্ড/API key সহ) থেকে যাবে — সত্যিকারের sensitive ডেটা হলে হিস্ট্রি থেকেও মুছতে হবে (দেখুন আইটেম ৯৪) এবং সাথে সাথে ওই key/secret rotate (পরিবর্তন) করতে হবে।

৪৮. gitignore আপডেটের পর cache ক্লিয়ার করা

সিনারিও (কী ঘটেছে): গিট ইগনোর (.gitignore) ফাইল আপডেট করেছেন, কিন্তু আগে থেকে ট্র্যাক হওয়া ফাইলগুলো এখনও ট্র্যাক হয়েই আছে, কারণ .gitignore শুধু নতুন/untracked ফাইলের ক্ষেত্রে কাজ করে।

কেন এটা হয় / ইন্টারনাল মেকানিজম: .gitignore ইনডেক্স-এ ইতিমধ্যে থাকা ফাইলকে প্রভাবিত করে না, শুধু নতুন untracked ফাইলকে “ignore” করে। git rm -r --cached . পুরো index খালি করে দেয় (working directory অক্ষত রেখে), তারপর git add . চালালে .gitignore অনুযায়ী নতুন করে সব ফাইল স্টেজ হয়।

সমাধান:

git rm -r --cached .
git add .
git commit -m "Apply updated .gitignore"

বাস্তব উদাহরণ: একজন ডেভেলপার প্রজেক্টের অনেক পরে বুঝতে পারেন build/ ফোল্ডার শুরু থেকেই ট্র্যাক হয়ে আসছে; .gitignore-এ যোগ করার পরও git status-এ সেটা modified দেখানো বন্ধ হচ্ছিল না, যতক্ষণ না তিনি পুরো cache রিসেট করেন।

৪৯. reset –hard-এর পর হারানো কমিট রিকভার করা

সিনারিও (কী ঘটেছে): গিট রি-সেট (reset --hard) করে ভুল করে সব ডিলিট করে ফেলেছেন! হারানো কমিট রিকভার করতে চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: reset --hard commit object-গুলোকে আসলে ডিলিট করে না — শুধু ব্রাঞ্চ ref-কে সরিয়ে দেয়, ফলে পুরনো commit ব্রাঞ্চ থেকে “unreachable” হয়ে যায়। কিন্তু গিট এমন প্রতিটা HEAD movement reflog-এ লগ করে রাখে, তাই সেখান থেকে পুরনো commit-এর হ্যাশ খুঁজে পাওয়া যায় (যতক্ষণ না git gc সেটা garbage-collect করে ফেলে, ডিফল্টভাবে ৩০-৯০ দিন সময় থাকে)।

সমাধান:

git reflog
git reset --hard <recovered-hash>

বাস্তব উদাহরণ: ভুল কমান্ডে দুই দিনের কাজ মুছে গেছে ভেবে আতঙ্কিত হওয়া একজন ডেভেলপার git reflog চালিয়ে দেখেন reset করার ঠিক আগের commit hash-টা এখনো তালিকায় আছে; সেই হ্যাশে reset --hard করে সব কাজ ফেরত পান।

৫০. আনট্র্যাকড ফাইল ক্লিনআপ করা

সিনারিও (কী ঘটেছে): আনট্র্যাকড (নতুন তৈরি করা কিন্তু add না করা) ফাইল বা ফোল্ডার ডিলিট করে ওয়ার্কিং ডিরেক্টরি পরিষ্কার (ক্লিনআপ) করতে চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: clean working directory স্ক্যান করে যেসব ফাইল গিট কখনো ট্র্যাক করেনি (index-এ নেই) সেগুলো খুঁজে সরাসরি ডিস্ক থেকে ডিলিট করে দেয় — -f (force) ছাড়া গিট সেফটির জন্য এটা করতে দেয় না, -d ফোল্ডারও ধরে।

সমাধান:

git clean -n   # প্রথমে dry-run করে দেখুন কী কী ডিলিট হবে
git clean -fd
git clean -fdx   # .gitignore-এ থাকা ফাইলসহ (যেমন node_modules, build artifacts)

বাস্তব উদাহরণ: একজন ডেভেলপার build টুল চালিয়ে অনেকগুলো টেম্পোরারি ফাইল ও ফোল্ডার জমিয়ে ফেলেছেন যেগুলো আর দরকার নেই; git clean -n দিয়ে আগে লিস্ট দেখে নিশ্চিত হয়ে git clean -fd চালিয়ে পরিষ্কার করেন। সতর্কতা: এটি destructive এবং ফাইল ট্র্যাশে না গিয়ে সরাসরি স্থায়ীভাবে ডিলিট হয়ে যায় — কোনো commit বা reflog-এ এর ব্যাকআপ থাকে না, তাই সবসময় আগে -n (dry-run) দিয়ে চেক করে নেওয়া উচিত।

📦 ক্যাটাগরি ৬: স্ট্যাশিং (Stashing) - অসমাপ্ত কাজ লুকিয়ে রাখা (৫১-৬০)

৫১. কাজ অর্ধেক রেখে স্ট্যাশ করা

সিনারিও (কী ঘটেছে): কাজ অর্ধেক হয়েছে, কিন্তু হঠাৎ অন্য ব্রাঞ্চে যেতে হবে। বর্তমান পরিবর্তনগুলো কমিট না করেই লুকিয়ে রাখতে চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: git stash working directory আর index-এর বর্তমান অবস্থা নিয়ে একটা বিশেষ ধরনের commit (আসলে দুই বা তিনটে commit-এর একটা ছোট স্ট্যাক) বানিয়ে refs/stash-এ সংরক্ষণ করে, তারপর working directory-কে HEAD-এর অবস্থায় ফিরিয়ে ক্লিন করে দেয় — ফলে git switch নিরাপদে করা যায়।

সমাধান:

git stash

বাস্তব উদাহরণ: একটা ফিচারের মাঝপথে থাকা অবস্থায় একজন ডেভেলপারকে অন্য একটা জরুরি ব্রাঞ্চ চেক করতে বলা হয়; git stash চালিয়ে working directory ক্লিন করে নিরাপদে ব্রাঞ্চ বদলান।

৫২. স্ট্যাশ পপ করে কাজ ফেরত আনা

সিনারিও (কী ঘটেছে): লুকানো (Stashed) কাজগুলো আবার বর্তমান ব্রাঞ্চে ফেরত আনতে চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: pop স্ট্যাশ কমিটের পরিবর্তন working directory ও index-এ পুনরায় apply করে, এবং সফল হলে refs/stash থেকে ওই এন্ট্রি মুছে দেয়। conflict হলে stash এন্ট্রিটা লিস্টে থেকে যায়, যাতে হারিয়ে না যায়।

সমাধান:

git stash pop

বাস্তব উদাহরণ: হটফিক্স শেষ করার পর একজন ডেভেলপার আগের ফিচার-ব্রাঞ্চে ফিরে git stash pop চালিয়ে ঠিক যেখানে কাজ থামিয়েছিলেন সেখান থেকেই আবার শুরু করেন।

৫৩. স্ট্যাশ apply করা (লিস্ট থেকে না মুছে)

সিনারিও (কী ঘটেছে): লুকানো কাজ ফেরত আনতে চান, কিন্তু স্ট্যাশ লিস্ট থেকে সেটা মুছতে চান না — যেমন একই স্ট্যাশ একাধিক ব্রাঞ্চে apply করতে চাইলে।

কেন এটা হয় / ইন্টারনাল মেকানিজম: apply ঠিক pop-এর মতোই পরিবর্তন working directory-তে বসিয়ে দেয়, শুধু পার্থক্য এই যে refs/stash-এর এন্ট্রিটা ডিলিট করা হয় না — একই স্ট্যাশ পরে আবার apply করা যায়।

সমাধান:

git stash apply

বাস্তব উদাহরণ: একটা কনফিগারেশন পরিবর্তন একাধিক ব্রাঞ্চে টেস্ট করার দরকার হওয়ায় একজন ডেভেলপার git stash apply দিয়ে প্রতিটা ব্রাঞ্চে একই পরিবর্তন বসিয়ে দেখেন, স্ট্যাশ লিস্ট থেকে না মুছেই।

৫৪. স্ট্যাশ লিস্ট দেখা

সিনারিও (কী ঘটেছে): কয়টি অসমাপ্ত কাজ স্ট্যাশ করে রেখেছেন তার লিস্ট দেখতে চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: refs/stash আসলে একটা reflog-এর মতো স্ট্যাক — প্রতিটা git stash একটা নতুন এন্ট্রি (stash@{0}, stash@{1}, …) যোগ করে সবচেয়ে সাম্প্রতিকটা উপরে (index 0) রাখে। list কমান্ড সেই স্ট্যাকটা দেখায়।

সমাধান:

git stash list

বাস্তব উদাহরণ: কয়েক সপ্তাহ ধরে টুকটাক কাজ স্ট্যাশ করে রাখা একজন ডেভেলপার git stash list চালিয়ে দেখেন পাঁচটা ভিন্ন স্ট্যাশ জমে আছে, কোনটা কী কাজের তা যাচাই করতে থাকেন।

৫৫. আনট্র্যাকড ফাইলসহ স্ট্যাশ করা

সিনারিও (কী ঘটেছে): আনট্র্যাকড ফাইলগুলোসহ (নতুন তৈরি করা ফাইল, যা এখনো add হয়নি) স্ট্যাশ করতে চান — ডিফল্টভাবে git stash এই ফাইলগুলো ধরে না।

কেন এটা হয় / ইন্টারনাল মেকানিজম: ডিফল্ট stash শুধু ট্র্যাক করা ফাইলের পরিবর্তন নেয়, কারণ untracked ফাইল index-এ থাকে না। -u (--include-untracked) ফ্ল্যাগ untracked ফাইলগুলোও অস্থায়ীভাবে stash commit-এ যোগ করে working directory থেকে সরিয়ে নেয়।

সমাধান:

git stash -u

বাস্তব উদাহরণ: একজন ডেভেলপার একটা নতুন কম্পোনেন্ট ফাইল তৈরি করেছেন কিন্তু এখনো add করেননি, এমন সময় ব্রাঞ্চ বদলাতে হয়; শুধু git stash দিলে নতুন ফাইলটা থেকেই যেত, তাই git stash -u ব্যবহার করেন যাতে সেটাও লুকিয়ে যায়।

৫৬. মেসেজসহ স্ট্যাশ করা

সিনারিও (কী ঘটেছে): নির্দিষ্ট একটি মেসেজ দিয়ে স্ট্যাশ করতে চান, যাতে পরে অনেকগুলো স্ট্যাশের মধ্যে মনে থাকে কোনটায় কী কাজ লুকিয়েছিলেন।

কেন এটা হয় / ইন্টারনাল মেকানিজম: স্ট্যাশ commit-এও একটা মেসেজ ফিল্ড থাকে, ডিফল্টভাবে গিট নিজে থেকে “WIP on branch: …” বসিয়ে দেয়; save/push -m দিয়ে সেই মেসেজ নিজের মতো কাস্টমাইজ করা যায়, যা stash list-এ দেখা যায়।

সমাধান:

git stash push -m "Working on header"
# পুরনো সিনট্যাক্স:
git stash save "Working on header"

বাস্তব উদাহরণ: একসাথে তিনটা ভিন্ন ছোট কাজ স্ট্যাশ করে রাখা একজন ডেভেলপার প্রতিটাতে স্পষ্ট মেসেজ দেন (“Working on header”, “CSS experiment”), যাতে সপ্তাহখানেক পরেও stash list দেখে ঠিক কোনটা দরকার তা বুঝতে পারেন।

৫৭. নির্দিষ্ট স্ট্যাশ apply করা

সিনারিও (কী ঘটেছে): লিস্টের নির্দিষ্ট একটি স্ট্যাশ (যেমন ২ নাম্বারটা) ফেরত আনতে চান, সবচেয়ে সাম্প্রতিকটা নয়।

কেন এটা হয় / ইন্টারনাল মেকানিজম: stash@{N} সিনট্যাক্স দিয়ে stash reflog-এর নির্দিষ্ট index-এর এন্ট্রিটা রেফার করা যায় — না দিলে গিট ডিফল্টভাবে stash@{0} (সবচেয়ে নতুনটা) ধরে নেয়।

সমাধান:

git stash apply stash@{2}

বাস্তব উদাহরণ: পাঁচটা স্ট্যাশ জমে থাকা অবস্থায় একজন ডেভেলপার git stash list দেখে বুঝতে পারেন তার দরকারি কাজটা তিন নম্বরে (stash@{2}) আছে; সরাসরি সেটাই apply করেন, বাকিগুলো স্পর্শ না করে।

৫৮. নির্দিষ্ট স্ট্যাশ ড্রপ করা

সিনারিও (কী ঘটেছে): একটি নির্দিষ্ট স্ট্যাশ লিস্ট থেকে ডিলিট করতে চান — যেমন কাজটা আর দরকার নেই।

কেন এটা হয় / ইন্টারনাল মেকানিজম: drop refs/stash reflog থেকে নির্দিষ্ট এন্ট্রিটা সরিয়ে দেয়; কমিট object টা সাথে সাথে গায়েব হয় না (কিছুদিন dangling object হিসেবে থাকে) কিন্তু কোনো ref থেকে reachable না থাকায় শীঘ্রই git gc-এর মাধ্যমে মুছে যাবে।

সমাধান:

git stash drop stash@{1}

বাস্তব উদাহরণ: পুরনো একটা এক্সপেরিমেন্টাল কাজের স্ট্যাশ আর দরকার নেই বুঝে একজন ডেভেলপার git stash drop stash@{1} চালিয়ে সেটা মুছে ফেলেন, বাকি স্ট্যাশগুলো ঠিক রেখে। সতর্কতা: ড্রপ করার পর সাধারণ উপায়ে সেই স্ট্যাশ ফেরত পাওয়া কঠিন — নিশ্চিত হয়ে drop করুন।

৫৯. সব স্ট্যাশ ক্লিয়ার করা

সিনারিও (কী ঘটেছে): স্ট্যাশ করে রাখা সব কাজ একেবারে মুছে ফেলতে চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: clear পুরো refs/stash reflog খালি করে দেয় — একবারে সব স্ট্যাশ এন্ট্রি হারিয়ে যায়, প্রতিটাকে আলাদাভাবে drop করার দরকার পড়ে না।

সমাধান:

git stash clear

বাস্তব উদাহরণ: ল্যাপটপ পরিষ্কার করার সময় একজন ডেভেলপার বুঝতে পারেন পুরনো দশটার বেশি স্ট্যাশের একটাও আর দরকার নেই; git stash clear দিয়ে একসাথে সব মুছে ফেলেন। সতর্কতা: এটি destructive এবং সব stash-এর কাজ একসাথে হারিয়ে যায়, স্বাভাবিক উপায়ে ফেরত পাওয়া যায় না — আগে git stash list দিয়ে নিশ্চিত হয়ে নিন সত্যিই সবগুলো বাতিলযোগ্য।

৬০. স্ট্যাশ থেকে নতুন ব্রাঞ্চ তৈরি করা

সিনারিও (কী ঘটেছে): স্ট্যাশ করা কাজগুলো নিয়ে একটি সম্পূর্ণ নতুন ব্রাঞ্চ তৈরি করতে চান — বিশেষ করে যখন stash করার পর থেকে মূল ব্রাঞ্চে অনেক পরিবর্তন হয়ে গেছে এবং সরাসরি apply করলে conflict হতে পারে।

কেন এটা হয় / ইন্টারনাল মেকানিজম: stash branch স্ট্যাশ যে commit-এর উপর ভিত্তি করে বানানো হয়েছিল, ঠিক সেই commit থেকে একটা নতুন ব্রাঞ্চ তৈরি করে, তারপর সেই ব্রাঞ্চে স্ট্যাশটা apply করে ও drop করে — এতে বর্তমান ব্রাঞ্চের সাথে diverge হয়ে যাওয়া কনফ্লিক্টের ঝুঁকি কমে।

সমাধান:

git stash branch <new-branch-name>

বাস্তব উদাহরণ: একজন ডেভেলপার একটা কাজ stash করে রাখার পর main ব্রাঞ্চে অনেক নতুন কমিট চলে আসে; সরাসরি pop করলে conflict হওয়ার শঙ্কায় তিনি git stash branch resume-work চালিয়ে একটা পরিষ্কার ব্রাঞ্চে কাজ চালিয়ে যান।

🔍 ক্যাটাগরি ৭: হিস্ট্রি ও লগ (History & Logging) (৬১-৭০)

৬১. সব কমিটের লগ দেখা

সিনারিও (কী ঘটেছে): আগের সব কমিটের লিস্ট দেখতে চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: git log HEAD থেকে শুরু করে প্রতিটা commit-এর parent লিংক অনুসরণ করে পেছনের দিকে হেঁটে পুরো হিস্ট্রি (author, তারিখ, মেসেজ, হ্যাশ) প্রিন্ট করে।

সমাধান:

git log

বাস্তব উদাহরণ: কোনো ফিচার কবে শুরু হয়েছিল তা মনে করার জন্য একজন ডেভেলপার git log চালিয়ে ধীরে ধীরে কমিট মেসেজগুলো স্ক্রল করে দেখেন।

৬২. one-line লগ দেখা

সিনারিও (কী ঘটেছে): কমিটগুলো খুব সংক্ষেপে (এক লাইনে) দেখতে চান, দ্রুত স্ক্যান করার জন্য।

কেন এটা হয় / ইন্টারনাল মেকানিজম: --oneline প্রতিটা commit-এর জন্য শুধু সংক্ষিপ্ত (৭ ক্যারেক্টার) হ্যাশ আর সাবজেক্ট লাইন দেখায়, ফুল মেসেজ বডি, author, তারিখ বাদ দিয়ে।

সমাধান:

git log --oneline

বাস্তব উদাহরণ: একটা লম্বা ফিচার-ব্রাঞ্চে কতগুলো কমিট জমেছে তা দ্রুত বোঝার জন্য একজন ডেভেলপার git log --oneline চালিয়ে এক নজরে পুরো লিস্ট দেখে নেন।

৬৩. শেষ কয়েকটা কমিট দেখা

সিনারিও (কী ঘটেছে): শুধু শেষ ৩টি কমিট দেখতে চান, পুরো হিস্ট্রি নয়।

কেন এটা হয় / ইন্টারনাল মেকানিজম: -N ফ্ল্যাগ শুধু নির্দিষ্ট সংখ্যক সাম্প্রতিক commit-এর পর log iteration থামিয়ে দেয় — পুরো হিস্ট্রি স্ক্যান করে না।

সমাধান:

git log -3

বাস্তব উদাহরণ: কমিট করার ঠিক পরে নিশ্চিত হতে যে সব ঠিকমতো সেভ হয়েছে, একজন ডেভেলপার git log -3 চালিয়ে শুধু সাম্প্রতিক তিনটা কমিট চেক করেন।

৬৪. গ্রাফসহ ভিজ্যুয়াল হিস্ট্রি দেখা

সিনারিও (কী ঘটেছে): কমিটের গ্রাফ, ব্রাঞ্চিং এবং মার্জিংয়ের ভিজ্যুয়াল হিস্ট্রি দেখতে চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: --graph ASCII লাইন এঁকে commit-দের parent-child ও merge সম্পর্ক ভিজ্যুয়ালি দেখায় — কোথায় ব্রাঞ্চ হয়েছিল, কোথায় merge হয়েছিল তা স্পষ্ট বোঝা যায়; --all সব ব্রাঞ্চ একসাথে দেখায়।

সমাধান:

git log --graph --oneline --all

বাস্তব উদাহরণ: টিমের একজন সিনিয়র ডেভেলপার নতুন জয়েন করা কাউকে প্রজেক্টের ব্রাঞ্চিং স্ট্র্যাটেজি বোঝাতে git log --graph --oneline --all চালিয়ে টার্মিনালেই ভিজ্যুয়ালি দেখিয়ে দেন কোন ফিচার কখন merge হয়েছিল।

৬৫. নির্দিষ্ট অথরের কমিট দেখা

সিনারিও (কী ঘটেছে): নির্দিষ্ট একজন ডেভেলপারের (যেমন John) সব কমিট দেখতে চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: --author প্রতিটা commit object-এ এমবেড থাকা author name/email ফিল্ডের সাথে দেওয়া প্যাটার্ন ম্যাচ করে ফিল্টার করে।

সমাধান:

git log --author="John"

বাস্তব উদাহরণ: একটা মডিউলে বাগ ধরা পড়ার পর কে কে ওই ফাইলে কাজ করেছেন বোঝার জন্য একজন টিম লিড git log --author="John" চালিয়ে তার সব কমিট দেখেন।

৬৬. নির্দিষ্ট ফাইলের হিস্ট্রি দেখা

সিনারিও (কী ঘটেছে): একটি নির্দিষ্ট ফাইলে কবে কে কী কমিট করেছে দেখতে চান, পুরো প্রজেক্টের হিস্ট্রি নয়।

কেন এটা হয় / ইন্টারনাল মেকানিজম: ফাইলের পাথ দিলে গিট শুধু সেই commit গুলো দেখায় যেগুলো ওই ফাইলের tree entry পরিবর্তন করেছে — বাকি ফাইলে পরিবর্তন হওয়া commit বাদ পড়ে যায়।

সমাধান:

git log <file-name>
git log --follow <file-name>   # ফাইলের নাম রিনেম হয়ে থাকলেও পুরনো হিস্ট্রি ট্র্যাক করে

বাস্তব উদাহরণ: একটা কনফিগ ফাইলে অদ্ভুত একটা সেটিং কবে যোগ হয়েছিল বোঝার জন্য একজন ডেভেলপার git log config.py চালিয়ে সেই ফাইলের শুধুমাত্র প্রাসঙ্গিক কমিটগুলো দেখেন।

৬৭. তারিখ অনুযায়ী কমিট ফিল্টার করা

সিনারিও (কী ঘটেছে): নির্দিষ্ট দুই তারিখের মাঝখানের কমিট দেখতে চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: --since/--until প্রতিটা commit-এর committer date ফিল্ডের সাথে দেওয়া রেঞ্জ তুলনা করে ফিল্টার করে।

সমাধান:

git log --since="2023-10-01" --until="2023-10-31"

বাস্তব উদাহরণ: কোনো মাসে ঠিক কী কী পরিবর্তন হয়েছিল তা নিয়ে একটা রিলিজ-নোট বানানোর জন্য একজন ডেভেলপার নির্দিষ্ট তারিখের রেঞ্জ দিয়ে লগ ফিল্টার করেন।

৬৮. কমিট মেসেজে শব্দ খোঁজা

সিনারিও (কী ঘটেছে): কমিট মেসেজে নির্দিষ্ট কোনো শব্দ (যেমন “fix”) খুঁজতে চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: --grep প্রতিটা commit-এর মেসেজ স্ট্রিং-এর মধ্যে দেওয়া প্যাটার্ন (রেগুলার এক্সপ্রেশন সাপোর্ট করে) খুঁজে ম্যাচ করা commit গুলো দেখায়।

সমাধান:

git log --grep="fix"

বাস্তব উদাহরণ: কোনো নির্দিষ্ট বাগ কবে ফিক্স হয়েছিল তা মনে করতে না পেরে একজন ডেভেলপার git log --grep="payment fix" চালিয়ে সংশ্লিষ্ট কমিটটা খুঁজে বের করেন।

৬৯. diff-সহ ফাইলের লগ দেখা

সিনারিও (কী ঘটেছে): নির্দিষ্ট কোনো ফাইলে কোডের পরিবর্তন (diff) সহ লগ দেখতে চান, শুধু মেসেজ নয়।

কেন এটা হয় / ইন্টারনাল মেকানিজম: -p (--patch) প্রতিটা commit-এর মেসেজের পাশাপাশি সেই commit-এ ঠিক কোন লাইনগুলো যোগ/বাদ হয়েছিল তার পুরো diff-ও প্রিন্ট করে দেয়।

সমাধান:

git log -p <file-name>

বাস্তব উদাহরণ: একটা ফাংশনের লজিক ধীরে ধীরে কীভাবে বদলেছে তা বোঝার জন্য একজন ডেভেলপার git log -p utils.py চালিয়ে প্রতিটা কমিটের সাথে আসল কোড পরিবর্তনও দেখে নেন।

৭০. blame দিয়ে লাইনের মালিক বের করা

সিনারিও (কী ঘটেছে): কোন লাইনের কোড কে, কবে লিখেছে তা বের করতে চান (Blame)।

কেন এটা হয় / ইন্টারনাল মেকানিজম: git blame ফাইলের প্রতিটা বর্তমান লাইনের জন্য পেছনের দিকে হিস্ট্রি হেঁটে বের করে সবশেষ কোন commit-এ সেই লাইনটা শেষবার পরিবর্তিত/যোগ হয়েছিল, এবং সেই commit-এর author ও তারিখ পাশে দেখায়।

সমাধান:

git blame <file-name>
git blame -L 10,20 <file-name>   # শুধু নির্দিষ্ট লাইন রেঞ্জ দেখতে

বাস্তব উদাহরণ: একটা অদ্ভুত কন্ডিশনাল স্টেটমেন্ট দেখে কারণ বুঝতে না পেরে একজন ডেভেলপার git blame চালিয়ে দেখেন কোন কমিটে এটা যোগ হয়েছিল, তারপর সেই commit মেসেজ পড়ে প্রেক্ষাপট বুঝে নেন।

✂️ ক্যাটাগরি ৮: রিব্যাস এবং চেরি-পিক (Rebase & Cherry-pick) (৭১-৮০)

৭১. rebase দিয়ে লিনিয়ার হিস্ট্রি রাখা

সিনারিও (কী ঘটেছে): অন্য ব্রাঞ্চের (যেমন main) নতুন আপডেট আপনার ব্রাঞ্চে আনবেন, কিন্তু মার্জ কমিট মেসেজ না দিয়ে একদম লিনিয়ার হিস্ট্রি রাখতে চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: rebase বর্তমান ব্রাঞ্চের commit গুলোকে সাময়িকভাবে সরিয়ে রেখে target ব্রাঞ্চের (main) সর্বশেষ commit-এর উপর একে একে সেই commit-এর diff replay (নতুন commit হিসেবে re-apply) করে — প্রতিটা replay হওয়া commit-এর parent বদলে যায়, তাই commit hash-ও নতুন হয়ে যায়।

সমাধান:

git switch feature-x
git rebase main

বাস্তব উদাহরণ: এক সপ্তাহ ধরে একটা ফিচার-ব্রাঞ্চে কাজ করার পর main-এ অনেক নতুন কমিট চলে এসেছে; পুশ করার আগে একজন ডেভেলপার git rebase main চালিয়ে নিজের কমিটগুলো লেটেস্ট main-এর উপর সাজিয়ে নেন, যাতে merge history পরিষ্কার থাকে। সতর্কতা: যদি এই ব্রাঞ্চ ইতিমধ্যে পুশ করা থাকে এবং অন্য কেউ তার উপর কাজ করে, rebase করলে হিস্ট্রি ডাইভার্জ করবে এবং সবাইকে force-push পরবর্তী সমন্বয় করতে হবে — শুধু নিজস্ব/এখনো শেয়ার না করা ব্রাঞ্চে rebase করুন।

৭২. কনফ্লিক্ট সমাধানের পর rebase continue করা

সিনারিও (কী ঘটেছে): রিব্যাস করার সময় কনফ্লিক্ট হলো, ফাইল ঠিক করার পর রিব্যাস প্রক্রিয়া চালিয়ে যেতে চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: কনফ্লিক্ট হলে rebase মাঝপথে থেমে যায় এবং conflict marker-সহ ফাইল রেখে দেয়; ম্যানুয়ালি ঠিক করে add করার পর --continue পরবর্তী commit replay করা চালিয়ে যায়, একেবারে শুরু থেকে না করে।

সমাধান:

# কনফ্লিক্ট ফাইল ঠিক করুন, তারপর:
git add <resolved-file>
git rebase --continue

বাস্তব উদাহরণ: rebase চলাকালীন একটা ফাইলে conflict marker (<<<<<<<) দেখে একজন ডেভেলপার ম্যানুয়ালি সঠিক কোড রেখে git add করে git rebase --continue দিয়ে বাকি কমিটগুলোর replay চালিয়ে যান।

৭৩. rebase বাতিল করা

সিনারিও (কী ঘটেছে): রিব্যাস করার সময় অবস্থা খুব জটিল/খারাপ হয়ে গেলে পুরো রিব্যাস বাতিল করে আগের অবস্থায় ফিরতে চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: --abort in-progress rebase-এর সব অস্থায়ী স্টেট মুছে ব্রাঞ্চ ref-কে rebase শুরু হওয়ার ঠিক আগের commit-এ ফিরিয়ে দেয় — যেন rebase কখনো শুরুই হয়নি।

সমাধান:

git rebase --abort

বাস্তব উদাহরণ: একটার পর একটা কমিটে conflict আসতে থাকায় একজন ডেভেলপার বুঝতে পারেন সমাধান করতে করতে ভুল হয়ে যাচ্ছে; git rebase --abort দিয়ে সব বাতিল করে শান্ত মাথায় আবার শুরু করেন।

৭৪. commit squash করা

সিনারিও (কী ঘটেছে): আপনার করা শেষের ৩টি ছোট ছোট কমিটকে (যেমন “wip”, “fix typo”, “final fix”) একত্রিত (Squash) করে একটি পরিষ্কার কমিট বানাতে চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: ইন্টারঅ্যাকটিভ rebase (-i) একটা এডিটরে কমিটগুলোর তালিকা খুলে দেয়, যেখানে pick-কে squash/s-এ বদলালে সেই commit-টা তার ঠিক আগের commit-এর সাথে মিলিয়ে একটাই নতুন commit বানানো হয় — একাধিক ছোট commit-এর diff একসাথে যোগ হয়ে একটা commit object তৈরি হয়।

সমাধান:

git rebase -i HEAD~3
# এডিটরে দ্বিতীয় ও তৃতীয় কমিটের "pick" বদলে "squash" (বা "s") লিখুন

বাস্তব উদাহরণ: একটা ফিচার তৈরি করতে গিয়ে “wip”, “fix”, “actually fix now” — এমন ১০টা এলোমেলো কমিট জমিয়ে ফেলা একজন ডেভেলপার PR দেওয়ার আগে git rebase -i HEAD~10 দিয়ে সেগুলো একটা পরিষ্কার, অর্থবহ কমিটে squash করে নেন। সতর্কতা: squash করলে commit hash বদলে যায়; পুশ করা কমিটে squash করলে পরে force-push লাগবে — টিমের সাথে শেয়ার না করা ব্রাঞ্চেই এটা করা নিরাপদ।

৭৫. একটা নির্দিষ্ট কমিট cherry-pick করা

সিনারিও (কী ঘটেছে): অন্য একটি ব্রাঞ্চ থেকে শুধুমাত্র নির্দিষ্ট একটি কমিট কপি করে আপনার ব্রাঞ্চে আনতে চান, পুরো ব্রাঞ্চ merge না করে।

কেন এটা হয় / ইন্টারনাল মেকানিজম: cherry-pick টার্গেট commit-এর diff (patch) বের করে বর্তমান HEAD-এর উপর apply করে একটা নতুন commit বানায় — কনটেন্ট একই থাকলেও parent আলাদা হওয়ায় নতুন commit hash তৈরি হয়।

সমাধান:

git cherry-pick <commit-hash>

বাস্তব উদাহরণ: একটা ডেভেলপমেন্ট ব্রাঞ্চে থাকা জরুরি সিকিউরিটি ফিক্স দ্রুত প্রোডাকশন ব্রাঞ্চে দরকার হলে পুরো ব্রাঞ্চ merge না করে একজন সিনিয়র ডেভেলপার শুধু সেই একটা কমিট git cherry-pick <hash> দিয়ে নিয়ে আসেন।

৭৬. একাধিক কমিট cherry-pick করা

সিনারিও (কী ঘটেছে): অন্য ব্রাঞ্চ থেকে পরপর কয়েকটি নির্দিষ্ট কমিট একসাথে চেরি-পিক করতে চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: একাধিক হ্যাশ দিলে গিট একে একে প্রতিটার জন্য পৃথক diff apply করে পৃথক নতুন commit বানায় — কোনোটায় conflict হলে বাকিগুলোর processing থেমে যায়, --continue/--abort দিয়ে সামলাতে হয়।

সমাধান:

git cherry-pick <hash1> <hash2> <hash3>
# পরপর রেঞ্জের কমিট হলে:
git cherry-pick <hash1>^..<hash3>

বাস্তব উদাহরণ: একটা হটফিক্স ব্রাঞ্চে তিনটা সম্পর্কিত কমিট (বাগ ফিক্স + টেস্ট + ডকুমেন্টেশন) করা হয়েছিল; সবগুলো একসাথে main-এ আনতে ডেভেলপার তিনটা হ্যাশ একসাথে দিয়ে cherry-pick করেন।

৭৭. cherry-pick কনফ্লিক্টের পর continue করা

সিনারিও (কী ঘটেছে): চেরি-পিক করার পর কনফ্লিক্ট হলে সেটা সমাধান করে প্রক্রিয়া চালিয়ে যেতে চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: rebase-এর মতোই, conflict হলে cherry-pick থেমে যায় এবং marker রেখে দেয়; ফাইল ঠিক করে add করার পর --continue বাকি (একাধিক হ্যাশ দেওয়া থাকলে) commit-গুলোর processing চালিয়ে যায়।

সমাধান:

git add <resolved-file>
git cherry-pick --continue

বাস্তব উদাহরণ: cherry-pick করা কমিটে একটা লাইন conflict দেখা দিলে একজন ডেভেলপার সেটা ম্যানুয়ালি ঠিক করে git cherry-pick --continue দিয়ে বাকি কাজ শেষ করেন।

৭৮. cherry-pick বাতিল করা

সিনারিও (কী ঘটেছে): চেরি-পিক প্রক্রিয়া পুরোপুরি বাতিল করতে চান, কনফ্লিক্ট সমাধান না করেই।

কেন এটা হয় / ইন্টারনাল মেকানিজম: --abort in-progress cherry-pick-এর সব অস্থায়ী পরিবর্তন মুছে working directory ও HEAD-কে cherry-pick শুরুর ঠিক আগের অবস্থায় ফিরিয়ে দেয়।

সমাধান:

git cherry-pick --abort

বাস্তব উদাহরণ: cherry-pick করার পর দেখা যায় কমিটটা আসলে যে ব্রাঞ্চে আনা হচ্ছে তার সাথে সাংঘর্ষিক অনেক নির্ভরতা আছে; ডেভেলপার git cherry-pick --abort দিয়ে পুরোটা বাতিল করে ভিন্ন উপায় খোঁজেন।

৭৯. pull –rebase করা

সিনারিও (কী ঘটেছে): পুশ করার আগে লেটেস্ট কোড নামিয়ে merge commit না বানিয়ে rebase করতে চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: --rebase ফ্ল্যাগ pull-কে merge-এর বদলে rebase করতে বলে — আগে fetch করে রিমোটের নতুন commit আনে, তারপর আপনার লোকাল কমিটগুলো সেই লেটেস্ট commit-এর উপর replay করে, ফলে অপ্রয়োজনীয় merge commit তৈরি হয় না।

সমাধান:

git pull --rebase
# স্থায়ীভাবে ডিফল্ট আচরণ বানাতে:
git config --global pull.rebase true

বাস্তব উদাহরণ: একটা টিম যেখানে হিস্ট্রি লিনিয়ার রাখার নিয়ম আছে, সেখানে প্রতিটা ডেভেলপার git pull --rebase ব্যবহার করেন যাতে git log --graph-এ অপ্রয়োজনীয় “Merge branch ‘main’ of…” কমিটের ছড়াছড়ি না হয়।

৮০. rebase-এর পর force push করা

সিনারিও (কী ঘটেছে): নিজের ব্রাঞ্চ rebase করার পর সাধারণ push প্রত্যাখ্যাত হচ্ছে (কারণ লোকাল হিস্ট্রি রিমোটের সাথে ডাইভার্জ করেছে), তাই ফোর্স পুশ করতে চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: rebase/amend/squash-এর পর কমিট hash বদলে যায়, ফলে লোকাল ব্রাঞ্চ আর রিমোট ব্রাঞ্চ common history শেয়ার করলেও লোকাল কমিটগুলো রিমোটের “descendant” হিসেবে গণ্য হয় না — তাই সাধারণ push প্রত্যাখ্যাত হয়। --force নিঃশর্তে রিমোট ref আপনার লোকাল অবস্থায় ওভাররাইট করে দেয়; --force-with-lease আগে চেক করে দেখে নেয় রিমোটে আপনার পরে কেউ নতুন কিছু push করেছে কিনা, করলে push আটকে দেয়।

সমাধান:

git push origin <branch> --force-with-lease   # নিরাপদ বিকল্প, প্রথমে এটা ব্যবহার করুন
git push origin <branch> --force               # শুধু নিশ্চিত হলে

বাস্তব উদাহরণ: নিজের ফিচার-ব্রাঞ্চ rebase করার পর একজন ডেভেলপার git push করতে গিয়ে “rejected” এরর পান; যেহেতু ব্রাঞ্চটা শুধু তারই, তিনি git push --force-with-lease চালিয়ে নিরাপদে আপডেট করেন। সতর্কতা: force push অন্য কারো ইতিমধ্যে পুশ করা কমিট রিমোট থেকে মুছে দিতে পারে যদি তা ঠিকভাবে না করা হয় — শেয়ার্ড ব্রাঞ্চে (main/develop) কখনোই সাধারণ --force ব্যবহার করবেন না, প্রয়োজনে সবসময় --force-with-lease ব্যবহার করুন এবং টিমকে আগে থেকে জানিয়ে রাখুন।

🏷️ ক্যাটাগরি ৯: ট্যাগিং এবং রিলিজ (Tagging & Releases) (৮১-৯০)

৮১. নতুন ভার্সন ট্যাগ করা

সিনারিও (কী ঘটেছে): প্রজেক্টের নতুন ভার্সন রিলিজের জন্য একটি ট্যাগ (Tag) বানাতে চান (যেমন v1.0)।

কেন এটা হয় / ইন্টারনাল মেকানিজম: সাধারণ (lightweight) ট্যাগ আসলে refs/tags/v1.0 নামে একটা ফাইল, যা সরাসরি একটা নির্দিষ্ট commit-এর হ্যাশ ধরে রাখে — অনেকটা ব্রাঞ্চের মতোই, শুধু পার্থক্য এই যে ট্যাগ কখনো নিজে থেকে সামনে এগোয় না (নতুন commit করলেও ট্যাগ একই commit-এ আটকে থাকে)।

সমাধান:

git tag v1.0

বাস্তব উদাহরণ: একটা প্রোডাক্টের প্রথম স্টেবল রিলিজ প্রস্তুত হওয়ার পর একজন ডেভেলপার সেই কমিটে git tag v1.0 চালিয়ে ভবিষ্যতে সহজে “v1.0-এ কী ছিল” খুঁজে পাওয়ার জন্য একটা স্থায়ী রেফারেন্স রেখে দেন।

৮২. মেসেজসহ annotated ট্যাগ বানানো

সিনারিও (কী ঘটেছে): ট্যাগটিতে বিস্তারিত মেসেজ (কে বানিয়েছে, কবে, কেন) যুক্ত করতে চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: -a (annotated) ট্যাগ শুধু একটা ref ফাইল নয় — এটি নিজেই একটা পূর্ণাঙ্গ tag object তৈরি করে, যাতে tagger name/email, তারিখ, মেসেজ এবং GPG সিগনেচার (চাইলে) সংরক্ষিত থাকে। এই object commit-কে পয়েন্ট করে, আর refs/tags/v1.0 সেই tag object-কে পয়েন্ট করে।

সমাধান:

git tag -a v1.0 -m "Release version 1.0"

বাস্তব উদাহরণ: কোম্পানির রিলিজ প্রক্রিয়ায় প্রতিটা মেজর ভার্সনে annotated ট্যাগ ব্যবহারের নিয়ম রাখা হয়, যাতে পরে git show v1.0 চালিয়ে ঠিক কে, কবে, কোন উদ্দেশ্যে রিলিজটা ট্যাগ করেছিলেন তা জানা যায়।

৮৩. সব ট্যাগের লিস্ট দেখা

সিনারিও (কী ঘটেছে): প্রজেক্টের সব ট্যাগের (রিলিজ ভার্সনের) লিস্ট দেখতে চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: git tag (আর্গুমেন্ট ছাড়া) .git/refs/tags/ ডিরেক্টরিতে থাকা সব ট্যাগ ফাইলের নাম বর্ণানুক্রমিকভাবে লিস্ট করে দেখায়।

সমাধান:

git tag
git tag -l "v1.*"   # নির্দিষ্ট প্যাটার্নের ট্যাগ ফিল্টার করতে

বাস্তব উদাহরণ: একটা পুরনো প্রজেক্টে ঠিক কতগুলো ভার্সন রিলিজ হয়েছে তা জানতে একজন ডেভেলপার git tag চালিয়ে পুরো লিস্ট দেখে নেন।

৮৪. আগের কমিটে ট্যাগ লাগানো

সিনারিও (কী ঘটেছে): নির্দিষ্ট কোনো আগের কমিটে (বর্তমান HEAD নয়) ট্যাগ লাগাতে চান, কারণ ট্যাগ দেওয়ার আগেই সেই রিলিজ পয়েন্ট পার হয়ে গেছে।

কেন এটা হয় / ইন্টারনাল মেকানিজম: ট্যাগ কমান্ডে commit hash উল্লেখ করলে গিট HEAD-এর বদলে সেই নির্দিষ্ট commit-কে পয়েন্ট করে ট্যাগ বানায় — কোনো checkout বা branch পরিবর্তনের দরকার হয় না।

সমাধান:

git tag v1.1 <commit-hash>

বাস্তব উদাহরণ: রিলিজের কিছুদিন পর মনে পড়ে যে সেই সময়কার কমিটে ট্যাগ দেওয়া হয়নি; একজন ডেভেলপার git log থেকে সঠিক হ্যাশ খুঁজে বের করে পরে সেই কমিটে git tag v1.1 <hash> দিয়ে রেট্রোঅ্যাক্টিভলি ট্যাগ করেন।

৮৫. নির্দিষ্ট ট্যাগ পুশ করা

সিনারিও (কী ঘটেছে): একটি নির্দিষ্ট ট্যাগ গিটহাবে আপলোড করতে চান — ডিফল্টভাবে git push ট্যাগ পাঠায় না।

কেন এটা হয় / ইন্টারনাল মেকানিজম: ট্যাগ ব্রাঞ্চ থেকে আলাদা এক ধরনের ref, তাই সাধারণ git push এগুলো অন্তর্ভুক্ত করে না — নাম উল্লেখ করে দিলে গিট শুধু সেই ট্যাগ ref (আর প্রয়োজনে সংশ্লিষ্ট tag object) রিমোটে পাঠায়।

সমাধান:

git push origin v1.0

বাস্তব উদাহরণ: লোকালি ট্যাগ বানানোর পর একজন ডেভেলপার git push origin v1.0 চালিয়ে সেটা GitHub-এ পাঠান, যাতে GitHub Releases পেজে এই ভার্সনটা দেখা যায় এবং CI/CD পাইপলাইন সেই ট্যাগ দেখে ডিপ্লয় ট্রিগার করতে পারে।

৮৬. একসাথে সব ট্যাগ পুশ করা

সিনারিও (কী ঘটেছে): একসাথে সব লোকাল ট্যাগ গিটহাবে আপলোড করতে চান, একটা একটা করে না দিয়ে।

কেন এটা হয় / ইন্টারনাল মেকানিজম: --tags লোকালে থাকা সব ট্যাগ ref একসাথে রিমোটে পাঠিয়ে দেয়, যেগুলো এখনও রিমোটে নেই।

সমাধান:

git push origin --tags

বাস্তব উদাহরণ: একটা পুরনো প্রজেক্ট নতুন GitHub রিপোতে মাইগ্রেট করার সময় একজন ডেভেলপার সব ব্রাঞ্চ push করার পর git push origin --tags দিয়ে বছরের পর বছরের সব ভার্সন ট্যাগও একসাথে নিয়ে যান।

৮৭. লোকাল ট্যাগ ডিলিট করা

সিনারিও (কী ঘটেছে): একটি লোকাল ট্যাগ ডিলিট করতে চান — যেমন ভুল নামে বা ভুল কমিটে ট্যাগ দেওয়া হয়ে গেছে।

কেন এটা হয় / ইন্টারনাল মেকানিজম: -d .git/refs/tags/-এ থাকা নির্দিষ্ট ফাইলটা ডিলিট করে দেয় — শুধু লোকাল রেফারেন্স মুছে যায়, রিমোটে আগে থেকে push করা থাকলে সেখানে কোনো প্রভাব পড়ে না।

সমাধান:

git tag -d v1.0

বাস্তব উদাহরণ: টাইপো করে v1.o (o অক্ষর, শূন্য নয়) নামে ট্যাগ দিয়ে ফেলা একজন ডেভেলপার সেটা git tag -d v1.o দিয়ে মুছে সঠিক নামে আবার ট্যাগ করেন।

৮৮. রিমোট ট্যাগ ডিলিট করা

সিনারিও (কী ঘটেছে): গিটহাব (রিমোট) থেকেও একটি ট্যাগ ডিলিট করতে চান, শুধু লোকাল থেকে নয়।

কেন এটা হয় / ইন্টারনাল মেকানিজম: এটা push অপারেশনেরই একটা রূপ — রিমোটকে বলা হচ্ছে refs/tags/v1.0 ref-টা মুছে ফেলতে; লোকাল ট্যাগ (যদি এখনও থাকে) এতে প্রভাবিত হয় না, আলাদাভাবে -d দিয়ে মুছতে হয়।

সমাধান:

git push origin --delete v1.0

বাস্তব উদাহরণ: ভুল কমিটে রিলিজ ট্যাগ পুশ হয়ে যাওয়ার পর একজন ডেভেলপার git push origin --delete v1.0 চালিয়ে রিমোট থেকে সরিয়ে, সঠিক কমিটে আবার ট্যাগ করে নতুন করে পুশ করেন। সতর্কতা: ট্যাগটা যদি ইতিমধ্যে কোনো ডিপ্লয়মেন্ট পাইপলাইন বা অন্য কেউ ব্যবহার করে ফেলে থাকে, ডিলিট করার আগে টিমকে জানিয়ে নিন।

৮৯. নির্দিষ্ট ট্যাগের কোড চেকআউট করা

সিনারিও (কী ঘটেছে): একটি নির্দিষ্ট ট্যাগের কোড চেকআউট করতে চান (যেমন ভার্সন ১-এর কোড ঠিক কেমন ছিল দেখতে)।

কেন এটা হয় / ইন্টারনাল মেকানিজম: ট্যাগ যেহেতু একটা নির্দিষ্ট commit-কে পয়েন্ট করে, checkout করলে HEAD সরাসরি সেই commit-এ চলে যায় (branch নয়, তাই এটা “detached HEAD” স্টেট) এবং working directory সেই সময়কার স্ন্যাপশট দিয়ে রিপ্লেস হয়।

সমাধান:

git checkout v1.0
# নতুন কাজ শুরু করতে চাইলে সাথে সাথেই ব্রাঞ্চ বানিয়ে নিন:
git checkout -b hotfix-from-v1.0 v1.0

বাস্তব উদাহরণ: একটা পুরনো গ্রাহকের কাছে এখনও v1.0 চলছে এবং সেখানে একটা বাগ রিপোর্ট এসেছে; ডেভেলপার git checkout v1.0 দিয়ে সেই সময়কার কোডে গিয়ে বাগটা রিপ্রোডিউস করে দেখেন। সতর্কতা: detached HEAD অবস্থায় নতুন commit করলে সেটা কোনো ব্রাঞ্চের অংশ হয় না — ব্রাঞ্চ বদলে ফেললে সেই কমিট হারিয়ে যাওয়ার ঝুঁকি থাকে, তাই এখানে কাজ চালিয়ে যেতে হলে আগেই -b দিয়ে একটা নতুন ব্রাঞ্চ বানিয়ে নিন।

৯০. ট্যাগের বিস্তারিত তথ্য দেখা

সিনারিও (কী ঘটেছে): নির্দিষ্ট ট্যাগ সম্পর্কে বিস্তারিত তথ্য (কে বানিয়েছে, কবে, মেসেজ, আর সংশ্লিষ্ট commit-এর ডিটেইলস) দেখতে চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: annotated ট্যাগের ক্ষেত্রে git show প্রথমে tag object-এর তথ্য (tagger, তারিখ, মেসেজ) দেখায়, তারপর সেই tag যে commit-কে পয়েন্ট করছে তার তথ্য ও diff দেখায়।

সমাধান:

git show v1.0

বাস্তব উদাহরণ: একটা পুরনো রিলিজ নিয়ে প্রশ্ন এলে একজন ডেভেলপার git show v1.0 চালিয়ে দেখেন সেই ভার্সন ঠিক কবে, কার দ্বারা, কী মেসেজসহ রিলিজ করা হয়েছিল।

🕵️‍♂️ ক্যাটাগরি ১০: অ্যাডভান্সড ট্রাবলশুটিং এবং প্রো-টিপস (৯১-১০০)

৯১. bisect দিয়ে বাগ কমিট খুঁজে বের করা

সিনারিও (কী ঘটেছে): প্রজেক্টে একটি বাগ (Bug) ঢুকেছে, কিন্তু জানেন না শত শত কমিটের মধ্যে কোনটায় বাগটি তৈরি হয়েছে। বাইনারি সার্চ করে দ্রুত বের করতে চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: bisect একটা known-good আর known-bad commit-এর মাঝখানে বাইনারি সার্চ করে — প্রতিবার ঠিক মাঝের commit checkout করিয়ে জিজ্ঞেস করে “এখানে বাগ আছে (bad) নাকি নেই (good)”, তারপর সেই অনুযায়ী সার্চ রেঞ্জ অর্ধেক করে ফেলে। ১০০০ কমিট হলেও মাত্র প্রায় ১০ ধাপেই (log₂ 1000 ≈ 10) কালপ্রিট commit বের হয়ে যায়।

সমাধান:

git bisect start
git bisect bad                 # বর্তমান কমিটে বাগ আছে
git bisect good <old-commit>   # এই পুরনো কমিটে বাগ ছিল না
# গিট একটা কমিট checkout করবে, টেস্ট করে good/bad বলুন — শেষে:
git bisect reset

বাস্তব উদাহরণ: একটা ওয়েবসাইটে হঠাৎ চেকআউট পেজ ভেঙে গেছে, কিন্তু গত দুই সপ্তাহে ২০০-র বেশি কমিট হয়েছে; টিম লিড git bisect start চালিয়ে গিটকে ধাপে ধাপে good/bad বলে মাত্র আটবার টেস্ট করে সঠিক কালপ্রিট কমিটটা খুঁজে বের করেন।

৯২. রিস্টোর না করেই পুরনো ফাইল কনটেন্ট দেখা

সিনারিও (কী ঘটেছে): নির্দিষ্ট একটি কমিটে কোনো ফাইলে কী লেখা ছিল তা দেখতে চান, কিন্তু ফাইলটি working directory-তে রিস্টোর না করেই।

কেন এটা হয় / ইন্টারনাল মেকানিজম: commit:path সিনট্যাক্স গিটকে সরাসরি অবজেক্ট ডেটাবেস থেকে সেই commit-এর tree-তে থাকা নির্দিষ্ট ফাইলের blob কনটেন্ট বের করে প্রিন্ট করতে বলে — working directory বা HEAD-এ কোনো পরিবর্তন হয় না, এটা সম্পূর্ণ read-only অপারেশন।

সমাধান:

git show <commit-hash>:<file-name>
git show <commit-hash>:<file-name> > old-version.js   # ফাইলে সেভ করতে চাইলে

বাস্তব উদাহরণ: একটা ফাংশন এক মাস আগে কেমন ছিল তুলনা করতে চাওয়া একজন ডেভেলপার ব্রাঞ্চ না বদলিয়ে সরাসরি git show <old-hash>:src/auth.js চালিয়ে টার্মিনালেই পুরনো কোড দেখে নেন।

৯৩. shallow clone করা

সিনারিও (কী ঘটেছে): আপনার প্রজেক্টের হিস্ট্রি বিশাল হয়ে গেছে (হাজার হাজার কমিট), শুধু লেটেস্ট কমিটটুকু ক্লোন করে দ্রুত ডাউনলোড করতে চান (Shallow Clone)।

কেন এটা হয় / ইন্টারনাল মেকানিজম: --depth 1 শুধু সর্বশেষ commit আর তার সাথে সংশ্লিষ্ট tree/blob অবজেক্ট ডাউনলোড করে, পুরনো commit history বাদ দিয়ে দেয় — ফলে ডাউনলোডের সাইজ ও সময় অনেক কমে যায়, তবে git log চালালে পুরনো হিস্ট্রি দেখা যাবে না।

সমাধান:

git clone --depth 1 <repo-url>

বাস্তব উদাহরণ: একটা CI/CD পাইপলাইনে শুধু বিল্ড করার জন্য লেটেস্ট কোড দরকার, পুরো ১০ বছরের হিস্ট্রি নয়; DevOps ইঞ্জিনিয়ার git clone --depth 1 ব্যবহার করে পাইপলাইনের ক্লোন-ধাপ কয়েক গুণ দ্রুত করে দেন।

৯৪. history থেকে sensitive ডেটা স্থায়ীভাবে মুছে ফেলা

সিনারিও (কী ঘটেছে): প্রজেক্ট থেকে সম্পূর্ণভাবে একটি বড় ফাইল বা sensitive ডেটা (যেমন .env, API key, পাসওয়ার্ড) যা আগে কমিট হয়ে গিয়েছিল, তা পুরো হিস্ট্রি থেকে চিরতরে মুছে ফেলতে চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: শুধু নতুন commit-এ ফাইল ডিলিট করলে পুরনো commit-এর tree-তে সেই ফাইলের blob রয়েই যায় — object database থেকে সেটা মুছতে হলে প্রতিটা affected commit নতুন করে (নতুন hash দিয়ে) rewrite করতে হয়। আধুনিক ও অফিসিয়ালি সুপারিশকৃত টুল হলো git filter-repo — এটা পুরো হিস্ট্রি স্ক্যান করে নির্দিষ্ট ফাইল/প্যাটার্ন বাদ দিয়ে সব commit নতুন করে rewrite করে (দ্রুত ও নিরাপদ)। পুরনো git filter-branch কমান্ডটি এখন Git নিজেই ডকুমেন্টেশনে “deprecated/legacy, ধীরগতির ও ভুল-প্রবণ” বলে চিহ্নিত করেছে এবং filter-repo ব্যবহারের পরামর্শ দেয়; বিকল্প হিসেবে GUI-নির্ভর BFG Repo-Cleaner-ও জনপ্রিয়, বিশেষ করে বড় ফাইল বা secret দ্রুত মুছতে।

সমাধান:

# আধুনিক ও সুপারিশকৃত পদ্ধতি (আগে ইনস্টল করতে হবে: pip install git-filter-repo):
git filter-repo --path secrets.env --invert-paths

# লিগ্যাসি/পুরনো পদ্ধতি (এখন deprecated, নতুন কাজে এড়িয়ে চলুন):
git filter-branch --force --index-filter \
  "git rm --cached --ignore-unmatch secrets.env" --prune-empty --tag-name-filter cat -- --all

# বিকল্প: BFG Repo-Cleaner
# bfg --delete-files secrets.env

বাস্তব উদাহরণ: একটা স্টার্টআপের ডেভেলপার ভুলে একটা AWS secret key কমিট করে ফেলেছিলেন এবং সেটা তিন সপ্তাহ আগে থেকেই হিস্ট্রিতে আছে; শুধু নতুন কমিটে ফাইল ডিলিট করলে পুরনো কমিটে key থেকেই যাবে বুঝে টিম git filter-repo --path secrets.env --invert-paths চালিয়ে পুরো হিস্ট্রি থেকে ফাইলটা মুছে ফেলেন এবং সাথে সাথে AWS কনসোলে গিয়ে key-টা rotate (বাতিল করে নতুন key ইস্যু) করেন। সতর্কতা: এটি সম্পূর্ণ history rewrite করে — প্রতিটা পরবর্তী commit-এর hash বদলে যায়, তাই এরপর --force দিয়ে push করতে হবে এবং টিমের প্রত্যেককে তাদের লোকাল কপি মুছে repository পুনরায় clone করতে হবে (পুরনো ক্লোনের সাথে merge/pull করলে মুছে ফেলা ডেটা আবার ফিরে আসতে পারে)। শুধু ফাইল মুছলেই যথেষ্ট নয় — leak হওয়া যেকোনো secret/key অবিলম্বে rotate করা আবশ্যক।

৯৫. gitignore ব্লক করা ফাইল চেক করা

সিনারিও (কী ঘটেছে): কোন ফাইলটি গিট ইগনোর (.gitignore) ফাইলের কোন নিয়মের কারণে ব্লক (ignore) হচ্ছে তা চেক করতে চান, কারণ git add করলেও ফাইলটা স্টেজ হচ্ছে না।

কেন এটা হয় / ইন্টারনাল মেকানিজম: check-ignore -v ফাইলের পাথের সাথে .gitignore-এর প্রতিটা প্যাটার্ন (global, per-directory) মিলিয়ে দেখে ঠিক কোন ফাইলের কোন লাইনের নিয়মের কারণে সেটা ignore হচ্ছে তা রিপোর্ট করে।

সমাধান:

git check-ignore -v <file-name>

বাস্তব উদাহরণ: একজন ডেভেলপার একটা লগ ফাইল কমিট করতে চাইছেন কিন্তু git add করার পরও git status-এ সেটা দেখাচ্ছে না; git check-ignore -v logs/app.log চালিয়ে দেখেন .gitignore-এর *.log নিয়মটা এটাকে ব্লক করছে।

৯৬. worktree দিয়ে একাধিক ফোল্ডারে কাজ করা

সিনারিও (কী ঘটেছে): আপনি চান একই রিপোজিটরির একটা ভিন্ন ব্রাঞ্চ অন্য একটা আলাদা লোকাল ফোল্ডারে খুলে সমান্তরালে কাজ করতে, বারবার stash/switch না করে (যেমন একদিকে ফিচার আরেকদিকে হটফিক্স)।

কেন এটা হয় / ইন্টারনাল মেকানিজম: worktree add একই .git অবজেক্ট ডেটাবেস শেয়ার করে (ডুপ্লিকেট হয় না) কিন্তু আলাদা একটা working directory তৈরি করে, যেখানে ভিন্ন একটা ব্রাঞ্চ checkout করা থাকে — দুইটা ফোল্ডারই একই commit history ও object দেখে, শুধু working directory আলাদা।

সমাধান:

git worktree add ../hotfix-folder hotfix-branch
git worktree list   # সব worktree দেখতে

বাস্তব উদাহরণ: একজন ডেভেলপার একটা লম্বা রিফ্যাক্টরিং ফিচারে কাজ করছেন, এমন সময় জরুরি হটফিক্স আসে; পুরো কাজ stash না করে git worktree add ../hotfix hotfix-branch দিয়ে পাশে আরেকটা ফোল্ডার খুলে সেখানে হটফিক্স করে ফেলেন, মূল কাজের ফোল্ডার একদম অক্ষত থাকে।

৯৭. ডিফল্ট কমিট এডিটর সেট করা

সিনারিও (কী ঘটেছে): কমিট মেসেজ লেখার সময় টার্মিনালের ডিফল্ট এডিটর (প্রায়ই Vim, যা নতুনদের কাছে বিভ্রান্তিকর) এর বদলে পছন্দের এডিটর (যেমন VS Code) ওপেন হোক চান।

কেন এটা হয় / ইন্টারনাল মেকানিজম: যখন git commit-এ -m ফ্ল্যাগ দেওয়া হয় না, গিট core.editor কনফিগে সেট করা প্রোগ্রামটা চালু করে একটা টেম্পোরারি ফাইলে মেসেজ লিখতে দেয়, তারপর সেভ করে বন্ধ করলে সেই কনটেন্টকে commit মেসেজ হিসেবে ব্যবহার করে।

সমাধান:

git config --global core.editor "code --wait"

বাস্তব উদাহরণ: Vim-এ প্রথমবার আটকে গিয়ে বের হতে না পারা একজন নতুন ডেভেলপার core.editor-কে VS Code-এ পরিবর্তন করে দেওয়ার পর থেকে আরামে familiar এডিটরেই বিস্তারিত কমিট মেসেজ লিখতে পারেন।

৯৮. sparse-checkout দিয়ে monorepo-র অংশ ক্লোন করা

সিনারিও (কী ঘটেছে): বড় প্রজেক্টে (monorepo, যেখানে অনেকগুলো সার্ভিস/অ্যাপ একই রিপোতে থাকে) আপনার শুধু একটা নির্দিষ্ট ফোল্ডার/সার্ভিস দরকার, পুরো working directory-তে সব ফোল্ডার ডাউনলোড করে রাখতে চান না (Sparse Checkout)।

কেন এটা হয় / ইন্টারনাল মেকানিজম: পুরো commit history ও object ডেটাবেস ঠিকই ডাউনলোড হয়, কিন্তু sparse-checkout গিটকে বলে দেয় working directory-তে শুধু নির্দিষ্ট পাথগুলোই “materialize” (আসল ফাইল হিসেবে বসানো) করতে — বাকি ফোল্ডারের ফাইল ডিস্কে লেখা হয় না, ফলে working directory অনেক হালকা থাকে।

সমাধান:

git clone --no-checkout <repo-url>
cd <repo>
git sparse-checkout init --cone
git sparse-checkout set services/payment-service

বাস্তব উদাহরণ: একটা কোম্পানির ৫০টা মাইক্রোসার্ভিসের কোড একই monorepo-তে রাখা আছে; পেমেন্ট টিমের একজন ডেভেলপার sparse-checkout দিয়ে শুধু services/payment-service ফোল্ডার নিজের ডিস্কে নামিয়ে বাকি ৪৯টা সার্ভিসের কোড ছাড়াই কাজ চালিয়ে যান।

৯৯. Git alias/shortcut বানানো

সিনারিও (কী ঘটেছে): আপনার নিজের একটা গিট এলিয়াস (Shortcut) তৈরি করতে চান (যেমন: git co দিলে checkout কাজ করবে), যাতে ঘন ঘন ব্যবহৃত লম্বা কমান্ড বারবার পুরোটা টাইপ করতে না হয়।

কেন এটা হয় / ইন্টারনাল মেকানিজম: config alias.<name> .gitconfig-এ [alias] সেকশনে একটা এন্ট্রি যোগ করে; git <name> চালালে গিট প্রথমে বিল্ট-ইন কমান্ড খোঁজে, না পেলে alias টেবিলে খোঁজে এবং পেলে সেটাকে সংশ্লিষ্ট আসল কমান্ড দিয়ে replace করে চালায়।

সমাধান:

git config --global alias.co checkout
git config --global alias.st status
git config --global alias.lg "log --graph --oneline --all"

বাস্তব উদাহরণ: প্রতিদিন শতবার git status আর জটিল গ্রাফ-লগ কমান্ড টাইপ করতে ক্লান্ত হয়ে একজন সিনিয়র ডেভেলপার git st আর git lg alias বানিয়ে নেন, যা তার টাইপিং সময় উল্লেখযোগ্যভাবে কমিয়ে দেয়।

১০০. গিট ডাটাবেস clean ও optimize করা

সিনারিও (কী ঘটেছে): গিট ডাটাবেসের ভেতরের অবজেক্টগুলোর অবস্থা চেক করতে এবং dangling/অকেজো ডেটা মুছে পুরো রিপোজিটরি ক্লিনআপ ও অপটিমাইজ করতে চান — বিশেষ করে অনেক দিনের পুরনো, বড় রিপোতে।

কেন এটা হয় / ইন্টারনাল মেকানিজম: git gc (garbage collect) কোনো ref (branch/tag/stash/reflog) থেকে আর reachable নয় এমন loose object গুলো (যেমন drop করা stash, deleted branch-এর commit) মুছে ফেলে, এবং বাকি ছোট ছোট object-গুলোকে একসাথে কমপ্যাক্ট “packfile”-এ সংকুচিত করে — এতে রিপোজিটরির সাইজ কমে ও পারফরম্যান্স বাড়ে। --prune=now reflog-এর গ্রেস পিরিয়ড উপেক্ষা করে অবিলম্বে dangling object মুছে দেয়।

সমাধান:

git gc --prune=now
git count-objects -vH   # চালানোর আগে/পরে ডাটাবেসের সাইজ দেখতে

বাস্তব উদাহরণ: কয়েক বছরের পুরনো একটা রিপোজিটরি ক্লোন করতে অস্বাভাবিক সময় লাগছিল দেখে একজন DevOps ইঞ্জিনিয়ার সার্ভারে git gc --prune=now চালিয়ে বছরের পর বছরের ফেলে দেওয়া dangling commit ও ব্লব পরিষ্কার করে রিপোর সাইজ উল্লেখযোগ্যভাবে কমিয়ে আনেন। সতর্কতা: --prune=now চালানোর পর reflog-এর গ্রেস পিরিয়ড ছাড়াই dangling object মুছে যায়, তাই এটা চালানোর আগে নিশ্চিত হয়ে নিন কোনো “হারিয়ে যাওয়া” কমিট এখনও দরকার কিনা যা reflog-এর মাধ্যমে রিকভার করার প্রয়োজন হতে পারে।


এই ১০০টি সিনারিও আপনার গিট (Git) ব্যবহারের দক্ষতাকে একদম জিরো থেকে প্রো লেভেলে নিয়ে যাওয়ার জন্য যথেষ্ট! প্রতিটা সিনারিওর সাথে থাকা ইন্টারনাল মেকানিজম বুঝে নিলে শুধু কমান্ড মুখস্থ করা নয়, বরং Git আসলে ভেতরে কী করছে তা বোঝা সহজ হয়ে যায় — আর সেটাই একজন জুনিয়র ডেভেলপারকে সিনিয়র ডেভেলপারে পরিণত করে।

চাইলে এই পোস্টটা বুকমার্ক করে রাখতে পারেন quick-reference হিসেবে, আর internals নিয়ে আরও গভীরে, ধাপে ধাপে শিখতে চাইলে দেখুন আমাদের সম্পূর্ণ Git মাস্টারি জার্নি

Comments