git merge: ব্রাঞ্চ কীভাবে মেশাবেন এবং কনফ্লিক্ট কীভাবে মেটাবেন
git merge এক ব্রাঞ্চের ইতিহাস বর্তমান ব্রাঞ্চে যুক্ত করে: বর্তমান ব্রাঞ্চ পিছিয়ে না থাকলে Git শুধু পয়েন্টার সরায় (fast-forward), নইলে দুই প্যারেন্টসহ একটি merge কমিট বানায়। লক্ষ্য ব্রাঞ্চে গিয়ে git merge নাম চালান; কনফ্লিক্ট হলে ফাইল ঠিক করে git add ও git commit দিন, অথবা git merge --abort দিয়ে সব বাতিল করুন।
git merge কী করে
git merge নির্দিষ্ট ব্রাঞ্চের পরিবর্তন নিয়ে আপনি এখন যে ব্রাঞ্চে আছেন তার মধ্যে বসায়। দিকটি মনে রাখুন: মেশানো হয় "নিজের ভেতরে"। feature/login-কে main-এ আনতে আগে main-এ যেতে হয়, তারপর মেশানোর কমান্ড।
git switch main
git pull # সার্ভার থেকে সর্বশেষ main টেনে নেওয়া
git merge feature/login
Git লাইন ধরে হাতে কিছু কপি করে না। সে দুই ব্রাঞ্চের সাধারণ পূর্বপুরুষ (merge base) খোঁজে, দুই পক্ষের সঙ্গে তুলনা করে এবং পরিবর্তনগুলো জুড়ে দেয়। দুই পক্ষ আলাদা জায়গা বদলালে ফল স্বয়ংক্রিয়ভাবে আসে। একই জায়গা বদলালে কনফ্লিক্ট তৈরি হয়, এবং সিদ্ধান্ত আপনার।
দুটি মোড: fast-forward ও তিন-পক্ষীয় merge
Fast-forward: যখন merge কমিট লাগে না
ধরুন, main থেকে feature আলাদা হয়েছে, তাতে তিনটি কমিট হয়েছে, আর এই সময়ে main-এ কেউ কিছু যোগ করেনি। তাহলে main-এর ইতিহাস মূলত feature-এর ইতিহাসের শুরুর অংশ। Git main-এর পয়েন্টার feature-এর শেষ কমিটে সরিয়ে দেয়, নতুন কোনো কমিট বানায় না।
$ git merge feature
Updating 3f2a1c4..9b7e0d2
Fast-forward
src/auth.js | 42 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 42 insertions(+)
ইতিহাস সরলরেখায় থাকে, কিন্তু "এখানে feature নামের একটি ব্রাঞ্চ ছিল" তথ্যটি হারিয়ে যায়। কখনো এটি ভালো, কখনো নয়; নিচে --no-ff-এর অংশে আলোচনা আছে।
তিন-পক্ষীয় merge ও merge কমিট
দুই ব্রাঞ্চই সাধারণ পূর্বপুরুষের পর এগিয়ে গেলে পয়েন্টার সরানো যায় না। Git তখন তিন-পক্ষীয় merge চালায়: ভিত্তি, "আমাদের" দিক (বর্তমান ব্রাঞ্চ) এবং "তাদের" দিক (যে ব্রাঞ্চ মেশানো হচ্ছে)। ফলাফল একটি আলাদা merge কমিটে লেখা হয়, যার দুটি প্যারেন্ট।
$ git merge feature
Merge made by the 'ort' strategy.
src/auth.js | 42 ++++++++++++++++++++
src/router.js | 3 +-
2 files changed, 44 insertions(+), 1 deletion(-)
Git একটি এডিটর খুলবে, যেখানে লেখা থাকে Merge branch 'feature'। এটি রেখে দিতে পারেন, বা কেন মেশানো হলো তা যোগ করতে পারেন। গ্রাফটি দেখতে:
git log --oneline --graph --all
Git 2.34 থেকে ডিফল্ট কৌশল ort; তার আগে ছিল recursive। প্রতিদিনের কাজে কৌশল বদলানোর দরকার নেই।
git merge-এর কয়েকটি কাজের ফ্ল্যাগ
| ফ্ল্যাগ | কী করে | কখন কাজে লাগে |
|---|---|---|
--no-ff | fast-forward সম্ভব হলেও সবসময় merge কমিট বানায় | ইতিহাসে দেখতে চান কোন কমিট একটি ফিচার থেকে এসেছে |
--ff-only | শুধু পয়েন্টার সরিয়ে মেশায়, নইলে ত্রুটি দেয় | স্থানীয় ব্রাঞ্চ হালনাগাদে অপ্রয়োজনীয় merge কমিট এড়াতে |
--squash | ব্রাঞ্চের সব পরিবর্তন ইনডেক্সে রাখে, কমিট বা ব্রাঞ্চের সংযোগ বানায় না | একটি ফিচারকে একটি পরিষ্কার কমিটে পরিণত করতে |
--no-commit | merge চালায়, কিন্তু কমিটের আগে থামে | কমিটের আগে ফল দেখতে বা টেস্ট চালাতে |
-m "বার্তা" | merge কমিটের বার্তা নির্ধারণ করে | স্ক্রিপ্টে বা এডিটর ছাড়া |
--abort | চলমান merge বাতিল করে আগের অবস্থায় ফেরায় | কনফ্লিক্ট অনেক, নতুন করে শুরু করতে চান |
--continue | কনফ্লিক্ট মেটানোর পর merge শেষ করে | শেষ ধাপে git commit-এর বিকল্প |
-X ours / -X theirs | বিরোধী অংশে স্বয়ংক্রিয়ভাবে নিজের বা তাদের সংস্করণ নেয় | কোন সংস্করণ ঠিক তা নিশ্চিত জানলে (সাবধানে!) |
--no-ff: ফিচারের সীমানা ধরে রাখা
অনেক দল শুধু --no-ff দিয়ে ফিচার মেশায়। তখন গ্রাফে প্রতিটি কাজ একটি "বুদবুদ"-এর মতো দেখায়, এবং একটি git revert -m 1 <merge-কমিট> দিয়ে পুরো কাজ একবারে বাতিল করা যায়। Fast-forward-এ এমন কোনো সীমানা থাকে না, তখন প্রতিটি কমিট আলাদা করে ফেরাতে হয়।
--squash: দশটির বদলে একটি কমিট
git switch main
git merge --squash feature/login
git commit -m "ইমেইল ও পাসওয়ার্ড দিয়ে লগইন"
--squash-এর পর Git ব্রাঞ্চটিকে মেশানো বলে গণ্য করে না: git branch -d feature/login সতর্কবার্তা দিয়ে "not fully merged" বলে থামবে। সব ঠিক আছে নিশ্চিত হলে git branch -D দিয়ে মুছুন। আর squash-এর পর একই ব্রাঞ্চে কাজ চালাবেন না: পরের merge আগে মেশা পরিবর্তনগুলোই আবার আনবে, এবং প্রায় নিশ্চিতভাবে কনফ্লিক্ট হবে।
কনফ্লিক্ট মেটানো: ধাপে ধাপে
কনফ্লিক্ট কোনো দুর্ঘটনা নয়, স্বাভাবিক কাজের একটি অবস্থা। এর অর্থ একটাই: দুই ব্রাঞ্চ একই লাইন বদলেছে (অথবা এক পক্ষ ফাইল মুছেছে, যাতে অন্য পক্ষ কাজ করছিল), এবং কোন সংস্করণ ঠিক তা Git আন্দাজ করতে চায় না।
$ git merge feature/login
Auto-merging src/config.js
CONFLICT (content): Merge conflict in src/config.js
Automatic merge failed; fix conflicts and then commit the result.
ধাপ ১: কী কনফ্লিক্ট হচ্ছে দেখা
$ git status
On branch main
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: src/config.js
ধাপ ২: ফাইল সম্পাদনা
কনফ্লিক্টের ফাইলে Git মার্কার বসায়:
<<<<<<< HEAD
const TIMEOUT = 5000;
=======
const TIMEOUT = 10000;
>>>>>>> feature/login
উপরের অংশ আপনার বর্তমান ব্রাঞ্চ (HEAD), নিচের অংশ মেশানো ব্রাঞ্চ। সঠিকটি রাখুন (বা দুটো মিলিয়ে নিন), তিনটি মার্কার-লাইন মুছুন, এবং ফাইল সেভ করুন। সাধারণ ভুল: ফাইলের মাঝখানে কোথাও একটি মার্কার রয়ে যাওয়া; কমিটের আগে খুঁজে নিন — git diff --check বাকি মার্কার দেখিয়ে দেয়।
ধাপ ৩: সমাধান চিহ্নিত করে merge শেষ করা
git add src/config.js
git commit # বা git merge --continue
পুরো কনফ্লিক্ট যদি একপক্ষ বেছে নিলেই মিটে যায়, ফাইল হাতে সম্পাদনার দরকার নেই:
git checkout --ours src/config.js # বর্তমান ব্রাঞ্চের সংস্করণ রাখা
git checkout --theirs src/config.js # মেশানো ব্রাঞ্চের সংস্করণ নেওয়া
git add src/config.js
এডিটরে সহজে
VS Code, JetBrains IDE ও অন্য এডিটর কনফ্লিক্ট তিন কলামে দেখায় এবং "বর্তমানটি নিন / আসছেটি নিন / দুটোই নিন" বোতাম দেয়। git mergetool দিয়ে বাইরের টুলও যুক্ত করা যায়। একটি কাজের সেটিং: git config --global merge.conflictStyle zdiff3। তখন মার্কারে সাধারণ পূর্বপুরুষের সংস্করণও দেখা যায়, এবং বোঝা যায় কে কী বদলেছে।
মত বদলালে merge বাতিল
git merge --abort
কমান্ডটি ওয়ার্কিং ফোল্ডার ও ইনডেক্স git merge-এর আগের অবস্থায় ফেরায়। তাই merge-এর আগে কাজের ফোল্ডার পরিষ্কার রাখা ভালো: অসমাপ্ত পরিবর্তন কমিট করুন বা git stash-এ রাখুন, নইলে তাদের merge-এর ফল থেকে আলাদা করা কঠিন হবে।
merge করা কমিট কীভাবে ফেরাবেন
merge কমিট যদি এখনো সার্ভারে না গিয়ে থাকে, সবচেয়ে সহজ উপায় ব্রাঞ্চ পেছনে সরানো:
git reset --hard ORIG_HEAD
ORIG_HEAD Git বিপজ্জনক অপারেশনের আগে লিখে রাখে, merge-এর আগেও: সেটি ব্রাঞ্চের আগের অবস্থান। --hard অকমিট পরিবর্তন মুছে দেয়, তাই আগে git status দেখুন।
merge যদি শেয়ার্ড রিপোজিটরিতে চলে গিয়ে থাকে, ইতিহাস লেখা হয় না, বরং একটি উল্টো কমিট যোগ করা হয়:
git revert -m 1 a1b2c3d
-m 1 বলে, "মূল" হলো প্রথম প্যারেন্ট (যে ব্রাঞ্চে মেশানো হয়েছিল)। একটি ফাঁদ: এরপর Git ধরে নেয় ফিচারের কমিটগুলো মেশা হয়ে গেছে। পরে ওই ফিচারটি আবার মেশাতে চাইলে আগে revert-কমিটটিকেই revert করতে হবে।
দলগত কাজে merge
git pull = fetch + merge
সাধারণ git pull পরিবর্তন নামিয়ে রিমোট ব্রাঞ্চ লোকাল ব্রাঞ্চে মেশায়। আপনি ও সহকর্মী একই সময়ে কমিট করলে "Merge branch 'main' of ..." কমিট আসবে। এমন প্রযুক্তিগত merge এড়াতে git config --global pull.ff only সেট করুন (fast-forward সম্ভব না হলে pull থেমে যায়, সিদ্ধান্ত আপনার হাতে) অথবা git pull --rebase ব্যবহার করুন।
Pull request ও Merge বোতাম
GitHub ও GitLab-এ মেশানো সাধারণত pull/merge request-এর মাধ্যমে হয়। "Create a merge commit" বোতাম git merge --no-ff-এর সমতুল্য, "Squash and merge" squash-এর মতো, "Rebase and merge" rebase-এর মতো। কোনটি ব্যবহার হবে, তা দলের চুক্তি; প্রক্রিয়াটি কমান্ড লাইনের মতোই।
নিজের ব্রাঞ্চে main মেশানো
দীর্ঘজীবী ব্রাঞ্চ main থেকে সরে যেতে থাকে এবং কনফ্লিক্ট জমে। নিয়মিত মূল ব্রাঞ্চ নিজের দিকে টেনে নিন:
git switch feature/login
git fetch origin
git merge origin/main
এতে কনফ্লিক্ট আসার সঙ্গে সঙ্গে ছোট ছোট অংশে মেটে, এবং শেষ পর্যায়ে main-এ মেশানো মসৃণ হয়।
সাধারণ ভুল
- উল্টো দিকে মেশানো।
main-এ থেকেgit merge mainচালানো, বা একটি ফিচার অন্য ফিচারে মেশানো। মেশানোর আগেgit branch --show-currentদিয়ে বর্তমান ব্রাঞ্চ যাচাই করুন। - পুরনো ব্রাঞ্চের সঙ্গে মেশানো। এক সপ্তাহ হালনাগাদ না হওয়া স্থানীয়
mainমেশানো। আগেgit pull, তারপর merge। - «Your local changes would be overwritten by merge». একই ফাইলে অকমিট পরিবর্তন আছে। কমিট করুন, বা stash-এ রাখুন।
- «refusing to merge unrelated histories». ব্রাঞ্চগুলোর সাধারণ পূর্বপুরুষ নেই; যেমন লোকাল রিপোজিটরি
git init-এ তৈরি, অথচ রিমোটে README আছে। মেশানো সত্যিই দরকার হলে--allow-unrelated-historiesযোগ করুন। - কনফ্লিক্ট মার্কার সহ কমিট। বিল্ড
<<<<<<<-এ ভেঙে পড়ে। কমিটের আগেgit diff --checkও টেস্ট চালান।
সারসংক্ষেপ
git merge ব্রাঞ্চ মেশানোর নিরাপদ উপায়: এটি বিদ্যমান কমিট লেখে না, শুধু নতুন একটি কমিট যোগ করে (বা পয়েন্টার সরায়)। বর্তমান ব্রাঞ্চে মেশান, কাজের ফোল্ডার পরিষ্কার রাখুন, ফিচারের সীমানা দরকার হলে --no-ff, আর একটিমাত্র কমিট চাইলে --squash। কনফ্লিক্ট মেটে তিন ধাপে: ফাইল ঠিক করা, git add, git commit; আর git merge --abort সবসময় আপনাকে শুরুর বিন্দুতে ফেরাতে পারে। ব্রাঞ্চ মেশানো ও কনফ্লিক্ট মেটানো Git কোর্সে বাস্তব অনুশীলনে শেখানো হয়।
সাধারণ প্রশ্ন
git merge ও git pull-এর পার্থক্য কী?
git pull আগে সার্ভার থেকে পরিবর্তন আনে (git fetch), তারপর রিমোট ব্রাঞ্চ বর্তমানে মেশায় git merge বা, সেটিং অনুযায়ী, rebase দিয়ে। git merge শুধু স্থানীয়ভাবে যা আছে তা নিয়ে কাজ করে।
একসঙ্গে কয়েকটি ব্রাঞ্চ মেশানো যায় কি?
হ্যাঁ, git merge feature-a feature-b একটি অক্টোপাস merge করে, একাধিক প্যারেন্টসহ; তবে শুধু কনফ্লিক্ট না থাকলে। বাস্তবে ব্রাঞ্চগুলো সাধারণত একটি একটি করে মেশানো হয়।
কোন ব্রাঞ্চগুলো বর্তমানে মেশানো হয়ে গেছে, কীভাবে দেখব?
git branch --merged বর্তমানে মেশানো ব্রাঞ্চ দেখায়, আর git branch --no-merged যেগুলো এখনো মেশেনি। মেশানো ব্রাঞ্চ git branch -d দিয়ে নিরাপদে মোছা যায়।
মেশানোর আগে কী আসবে তা কীভাবে আগে থেকে দেখব?
git log main..feature দিয়ে দেখুন main-এ নেই এমন কমিট, এবং git diff main...feature দিয়ে সাধারণ পূর্বপুরুষের তুলনায় ব্রাঞ্চের পরিবর্তন।
merge-এর পর ORIG_HEAD কী?
এটি Git-এর রাখা একটি রেফারেন্স, যা merge-এর আগে ব্রাঞ্চের অবস্থান ধরে রাখে। সার্ভারে না পাঠানো পর্যন্ত git reset --hard ORIG_HEAD দিয়ে স্থানীয় merge ফেরানো যায়।
“শূন্য থেকে আত্মবিশ্বাসী ডেভেলপার পর্যন্ত গিট” কোর্সে বিষয়টি অনুশীলনসহ বিস্তারিত শেখানো হয়েছে।
কোর্সে যান