Git submodule: সাবমডিউল যোগ, আপডেট ও মুছে ফেলার গাইড
Git submodule হলো আপনার রিপোজিটরির ভেতরে যুক্ত একটি অন্য রিপোজিটরি, যা একটি নির্দিষ্ট কমিটে আটকানো থাকে। সাবমডিউল যোগ হয় git submodule add <url> <পথ> দিয়ে, ক্লোনের সময় আনা হয় git clone --recurse-submodules দিয়ে, আপডেট হয় git submodule update --remote দিয়ে, আর মুছে ফেলা হয় git submodule deinit ও git rm দিয়ে।
Git submodule (সাবমডিউল) হলো আপনার প্রজেক্ট ফোল্ডারের ভেতরে থাকা একটি আলাদা Git রিপোজিটরি। এর মূল বৈশিষ্ট্য: প্যারেন্ট রিপোজিটরি সাবমডিউলের ফাইল নয়, বরং তার একটি নির্দিষ্ট কমিটের রেফারেন্স রাখে। তাই প্রজেক্ট যে-ই ক্লোন করুক, সাবমডিউল একই ভার্সনে থাকবে — যতক্ষণ না আপনি নিজে সেটি স্পষ্টভাবে বদলান।
সাবমডিউল ব্যবহৃত হয় যখন প্যাকেজ ম্যানেজার ছাড়া বাইরের লাইব্রেরি যুক্ত করতে হয়, একাধিক প্রজেক্টের মধ্যে সাধারণ কোড ভাগ করতে হয়, অথবা থিম বা ফার্মওয়্যারকে নিজস্ব ইতিহাসসহ আলাদা রিপোজিটরিতে রাখতে হয়।
git submodule কীভাবে কাজ করে
সাবমডিউল তিনটি অংশ নিয়ে গঠিত, এবং এদের সম্পর্ক বুঝলে অধিকাংশ সমস্যা সহজে ধরা পড়ে:
- ফাইল
.gitmodulesপ্রজেক্টের মূলে — কমিট হয় এবং বলে দেয় কোন পথ কোন URL থেকে আসবে। - gitlink এন্ট্রি কমিটের ট্রিতে — বিশেষ উপাদান, মোড
160000, যেখানে কনটেন্টের বদলে থাকে সাবমডিউলের একটি কমিট-হ্যাশ। - সাবমডিউলের নিজের রিপোজিটরি — তার ডেটা প্যারেন্টের
.git/modules/<নাম>-এ থাকে, আর সাবমডিউল ফোল্ডারে একটি.gitফাইল থাকে যা সেখানে নির্দেশ করে।
cat .gitmodules
[submodule "libs/ui-kit"]
path = libs/ui-kit
url = https://github.com/example/ui-kit.git
git ls-tree HEAD libs/
160000 commit 8d7c6b5a4f3e2d1c0b9a8f7e6d5c4b3a2f1e0d9c libs/ui-kit
লক্ষ করুন: প্যারেন্ট একটি ব্রাঞ্চ নয়, একটি কমিট ধরে রাখে। এটিই submodule git-এর মূল পার্থক্য "main-এর সর্বশেষ ভার্সন" ধরনের নির্ভরতা থেকে।
সাবমডিউল কীভাবে যোগ করবেন
git submodule add https://github.com/example/ui-kit.git libs/ui-kit
Cloning into '/home/dev/shop/libs/ui-kit'...
done.
git status
new file: .gitmodules
new file: libs/ui-kit
git commit -m "ui-kit সাবমডিউল হিসেবে যুক্ত"
কমান্ডটি রিপোজিটরি ক্লোন করে, .gitmodules তৈরি বা আপডেট করে, এবং gitlink ইনডেক্সে যোগ করে। এরপর শুধু কমিট বাকি। পথ না দিলে ফোল্ডারের নাম হবে রিপোজিটরির নামে।
আপডেটের সময় সাবমডিউল একটি নির্দিষ্ট ব্রাঞ্চ অনুসরণ করুক চাইলে যোগ করার সময়ই দিন: git submodule add -b develop <url> <পথ>। ব্রাঞ্চটি .gitmodules-এ branch = develop হিসেবে লেখা থাকবে।
আপেক্ষিক URL
সাবমডিউল একই সার্ভারে থাকলে আপেক্ষিক পথ দিতে পারেন: git submodule add ../ui-kit.git libs/ui-kit। এটি প্যারেন্টের রিমোট URL-এর সাপেক্ষে বোঝা হয়, তাই প্রজেক্ট SSH বা HTTPS যেভাবেই ক্লোন করা হোক কাজ করে।
সাবমডিউলসহ প্রজেক্ট ক্লোন
সবচেয়ে সাধারণ অভিযোগ: "ক্লোন করেছি, কিন্তু সাবমডিউলের ফোল্ডার খালি।" ডিফল্টভাবে git clone সাবমডিউল আনে না। সঠিক উপায়:
git clone --recurse-submodules https://github.com/example/shop.git
প্রজেক্ট ইতিমধ্যে সাবমডিউল ছাড়া ক্লোন হয়ে থাকলে:
git submodule update --init --recursive
--init .gitmodules-এর সাবমডিউলগুলো স্থানীয় .git/config-এ নিবন্ধন করে, update সেগুলো নামিয়ে নির্ধারিত কমিটে নিয়ে যায়, আর --recursive সাবমডিউলের ভেতরের সাবমডিউলগুলোর জন্যও একই কাজ করে।
সাবমডিউল কীভাবে আপডেট করবেন
এখানে দুটি কাজ আলাদা করে ভাবা জরুরি: "সহকর্মীরা যে ভার্সন কমিট করেছেন সেটি আনা" এবং "লাইব্রেরির নতুন ভার্সনে যাওয়া"।
প্রজেক্টে রাখা ভার্সন আনা
git pull-এর পর প্যারেন্টের gitlink বদলাতে পারে, কিন্তু সাবমডিউলের ফাইল নিজে থেকে আপডেট হয় না — git status দেখাবে modified: libs/ui-kit (new commits)। সমন্বয় করুন:
git pull
git submodule update --init --recursive
হাতে না করে স্বয়ংক্রিয় করতে: git config submodule.recurse true। তখন pull, checkout ও switch নিজেরাই সাবমডিউল আপডেট করবে। একবারের জন্য এটি করতে চাইলে git pull --recurse-submodules।
লাইব্রেরির নতুন ভার্সনে যাওয়া
# বিকল্প ১: ট্র্যাক করা ব্রাঞ্চের সর্বশেষ কমিট আনা
git submodule update --remote libs/ui-kit
# বিকল্প ২: নিজে ভার্সন বেছে নেওয়া
cd libs/ui-kit
git fetch
git checkout v3.2.0
cd ../..
# দুই ক্ষেত্রেই — নতুন ভার্সনটি প্যারেন্টে স্থায়ী করা
git add libs/ui-kit
git commit -m "ui-kit আপডেট v3.2.0-এ"
--remote .gitmodules-এর branch প্যারামিটার থেকে ব্রাঞ্চ নেয়; সেটি না থাকলে সাবমডিউলের রিমোটের HEAD যে ব্রাঞ্চে আছে (সাধারণত main)। প্যারেন্টে কমিট না করলে সহকর্মীরা নতুন ভার্সন পাবেন না: সাবমডিউল আপডেট মানেই gitlink-এর পরিবর্তন, যা কমিট করতে হয়।
সাবমডিউলে কী বদলেছে দেখতে:
git diff --submodule=log
git config diff.submodule log # এই আউটপুটটিকেই ডিফল্ট করা
git submodule status
8d7c6b5a4f3e2d1c0b9a8f7e6d5c4b3a2f1e0d9c libs/ui-kit (v3.1.0)
+1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b libs/charts (v1.4.0-3-g1a2b3c4)
git submodule status-এর প্রথম চিহ্ন: - — সাবমডিউল আরম্ভ হয়নি, + — তার বর্তমান কমিট প্যারেন্টে রাখা কমিটের থেকে আলাদা, U — merge-কনফ্লিক্ট।
সাবমডিউলে পরিবর্তন কীভাবে আনবেন
git submodule update-এর পর সাবমডিউল detached HEAD অবস্থায় থাকে — একটি কমিটে, ব্রাঞ্চে নয়। এখান থেকে সরাসরি কমিট করলে পরের update-এ সেগুলো সহজে হারিয়ে যায়। কাজের ধাপ:
cd libs/ui-kit
git switch main # ব্রাঞ্চে যাওয়া
# ... পরিবর্তন ...
git commit -am "বোতামের প্যাডিং ঠিক করা"
git push # আগে সাবমডিউল push করুন!
cd ../..
git add libs/ui-kit
git commit -m "ui-kit: বোতামের প্যাডিং ঠিক"
git push
push-এর ক্রম গুরুত্বপূর্ণ। সাবমডিউল push না করে প্যারেন্ট আগে পাঠালে সার্ভারের প্রজেক্ট এমন একটি কমিটের দিকে নির্দেশ করবে, যা আপনি ছাড়া কেউ নামাতে পারবে না — সহকর্মীদের git submodule update fatal: remote error: upload-pack: not our ref বা did not contain ত্রুটিতে থামবে। সুরক্ষার জন্য git push --recurse-submodules=check (সাবমডিউল না পাঠালে push প্রত্যাখ্যান) অথবা =on-demand (সাবমডিউল আগে নিজে পাঠিয়ে দেবে) ব্যবহার করুন।
সাবমডিউল কীভাবে মুছবেন
আগে মুছতে কয়েকটি ফাইল হাতে বদলাতে হতো। আধুনিক Git-এ তিনটি ধাপই যথেষ্ট:
git submodule deinit -f libs/ui-kit # .git/config থেকে সরানো ও ফোল্ডার খালি করা
git rm -f libs/ui-kit # gitlink ও .gitmodules-এর এন্ট্রি সরানো
rm -rf .git/modules/libs/ui-kit # সংরক্ষিত সাবমডিউল ডেটা মোছা
git commit -m "ui-kit সাবমডিউল মুছে ফেলা"
শেষ ধাপ সঠিকতার জন্য বাধ্যতামূলক নয়, কিন্তু না করলে একই পথে আবার যোগ করার সময় Git অভিযোগ করতে পারে যে স্থানীয় ফোল্ডার আগে থেকেই আছে (A git directory for 'libs/ui-kit' is found locally)।
সাবমডিউলের URL বদলালে
রিপোজিটরি অন্য জায়গায় সরে গেলে .gitmodules ঠিক করুন এবং স্থানীয় কনফিগারেশন মেলান:
git submodule set-url libs/ui-kit https://gitlab.com/example/ui-kit.git
git submodule sync --recursive
git commit -am "ui-kit-এর নতুন ঠিকানা"
সহকর্মীদের pull-এর পর git submodule sync চালাতে হবে: .gitmodules-এর URL তাদের .git/config-এ শুধু init বা sync-এর সময় কপি হয়।
একাধিক সাবমডিউলের জন্য দরকারি কমান্ড
| কাজ | কমান্ড |
|---|---|
| তালিকা ও অবস্থা | git submodule status --recursive |
| প্রতিটিতে একটি কমান্ড চালানো | git submodule foreach 'git switch main && git pull' |
| দ্রুত নামানো | git submodule update --init --jobs 4 |
| ইতিহাস ছাড়া নামানো | git submodule update --init --depth 1 |
| status-এ সংক্ষিপ্ত সারাংশ | git config status.submoduleSummary true |
সাবমডিউলের সাধারণ সমস্যা
- ক্লোনের পর খালি ফোল্ডার — init করা হয়নি। সমাধান:
git submodule update --init --recursive। - pull-এর পরপরই "modified: (new commits)" — প্যারেন্ট অন্য কমিট দেখাচ্ছে, কিন্তু ফাইল পুরোনো। সমাধান:
git submodule update। - ভুলে সাবমডিউল পুরোনো করে ফেলা — সাবমডিউল পুরোনো কমিটে থাকা অবস্থায়
git add .করে ভার্সন ফেরানো কমিট হয়ে গেছে। কমিটের আগে সবসময়git diff --submoduleদেখুন। - merge-এ gitlink কনফ্লিক্ট — দুই ব্রাঞ্চে সাবমডিউল আলাদা কমিটে আপডেট হয়েছে। সাবমডিউলে গিয়ে প্রয়োজনীয় কমিট বাছুন (প্রায়ই নতুনটি, যেটিতে দুই পরিবর্তনই আছে), তারপর
git add পথদিয়ে merge শেষ করুন। - CI-তে ব্যক্তিগত সাবমডিউল — অ্যাক্সেস ছাড়া বিল্ড সাবমডিউল নামাতে পারে না। CI-কে সাবমডিউল রিপোজিটরির কী বা টোকেন দিতে হবে; আপেক্ষিক URL সেটআপ সহজ করে।
দলে সাবমডিউলের নিয়মাবলি
সাবমডিউলের বেশিরভাগ সমস্যা Git-এর কারণে নয়, অংশগ্রহণকারীরা ভিন্নভাবে কাজ করেন বলে হয়। প্রজেক্টের README-তে এই কয়েকটি নিয়ম লিখে রাখা ভালো:
- শুধু
--recurse-submodulesদিয়ে ক্লোন করুন এবং সবাইsubmodule.recurse trueচালু রাখুন। - সাবমডিউল আপডেট আলাদা কমিটে করুন, স্পষ্ট বার্তাসহ: কোন ভার্সন এবং কেন।
- প্যারেন্ট প্রজেক্টে সাবমডিউলের কোড না বদলে, আলাদা কমিট ও সাবমডিউল রিপোজিটরিতে push ছাড়া নয়।
- CI-তে স্পষ্টভাবে
git submodule update --init --recursiveচালান এবং যাচাই করুন বিল্ড স্থানীয় পরিবর্তনের ওপর নির্ভর করে না।
CI-তে যাচাই করতে যে সব কমিট সত্যিই পাওয়া যায়, একটি পরিষ্কার ক্লোনই যথেষ্ট: কেউ সাবমডিউল push করতে ভুলে গেলে বিল্ড update ধাপে ভাঙবে, সপ্তাহখানেক পরে সহকর্মীর মেশিনে নয়।
git clone --recurse-submodules --jobs 4 "$REPO_URL" build
cd build
git submodule status --recursive
সাবমডিউল নাকি বিকল্প
সাবমডিউল নির্দিষ্ট ভার্সনের ওপর সঠিক নিয়ন্ত্রণ ও আলাদা ইতিহাস দেয়, কিন্তু প্রতিটি অংশগ্রহণকারীর জন্য হাতে করার ধাপ বাড়ায়। কোড সাধারণ লাইব্রেরি হলে প্যাকেজ ম্যানেজার (npm, pip, Composer, Go modules) সহজ। বাইরের কোড প্রজেক্টের ভেতরে থাকবে এবং অতিরিক্ত কমান্ড ছাড়া ক্লোন হবে, এমন চাইলে git subtree দেখুন: এটি ইতিহাস মূল রিপোজিটরিতে কপি করে, আর সহকর্মীদের কিছু জানতে হয় না। অনেক সম্পর্কিত সার্ভিসের জন্য দলগুলো প্রায়ই মনোরেপো বেছে নেয়। সাবমডিউল তখনই উপযুক্ত, যখন ভেতরের কোডের নিজস্ব জীবন থাকে — আলাদা রিলিজ ও আলাদা দল।
সারসংক্ষেপ
Git submodule হলো একটি অন্য রিপোজিটরির নির্দিষ্ট কমিটের রেফারেন্স। যোগ করতে git submodule add, আনতে --recurse-submodules বা update --init, নতুন ভার্সনে নিতে update --remote ও প্যারেন্টে কমিট, মুছতে deinit, git rm ও .git/modules পরিষ্কার। সাবমডিউলের ভেতরের detached HEAD ও push-এর ক্রম মনে রাখুন। উন্নত Git সরঞ্জামগুলো কোর্সে হাতেকলমে শেখানো হয়।
সাধারণ প্রশ্ন
সাবমডিউল কেন সবসময় detached HEAD অবস্থায় থাকে?
প্যারেন্ট একটি ব্রাঞ্চ নয়, কমিট-হ্যাশ ধরে রাখে, আর git submodule update সাবমডিউলকে ঠিক সেই কমিটে নিয়ে যায়। সম্পাদনার জন্য হাতে ব্রাঞ্চে যান, অথবা git submodule update --remote --merge ব্যবহার করুন।
সাবমডিউলকে কি একটি ট্যাগে আটকানো যায়?
সরাসরি নয়, তবে সাবমডিউলে git checkout দিয়ে ট্যাগে যান এবং প্যারেন্টে সেই কমিট কমিট করুন; git submodule status-এ ট্যাগটি বন্ধনীতে দেখা যাবে।
GitHub-এর ZIP আর্কাইভে কি সাবমডিউল থাকে?
না, GitHub-এর সোর্স আর্কাইভে সাবমডিউলের কনটেন্ট থাকে না, ফোল্ডারগুলো খালি থাকবে। git clone --recurse-submodules ব্যবহার করুন।
git submodule আর git subtree-এর পার্থক্য কী?
submodule বাইরের রিপোজিটরির কমিটের রেফারেন্স রাখে এবং কোড আনতে আলাদা কমান্ড লাগে, আর subtree ফাইল ও ইতিহাস সরাসরি মূল রিপোজিটরিতে কপি করে।
সাবমডিউল কি অন্য ফোল্ডারে সরানো যায়?
হ্যাঁ, git mv পুরোনো/পথ নতুন/পথ দিয়ে: Git নিজেই .gitmodules ও অভ্যন্তরীণ রেফারেন্স আপডেট করবে, শুধু কমিট বাকি থাকবে।
“শূন্য থেকে আত্মবিশ্বাসী ডেভেলপার পর্যন্ত গিট” কোর্সে বিষয়টি অনুশীলনসহ বিস্তারিত শেখানো হয়েছে।
কোর্সে যান