পাঠ ৩৮ · ৫৮-এর মধ্যে · মডিউল ৮
Home / Courses / Software Engineering Principles & Git / Merge বনাম Rebase

মার্জ বনাম রিবেস — কখন কোনটা

Merge vs rebase — when to use which
৭ মিনিট পড়া মধ্যম · Intermediate Python কোডসহ সম্পূর্ণ বাংলায়

এই পাঠে যা শিখবেন

  • merge ও rebase-এর ফলে তৈরি হওয়া হিস্ট্রি-আকৃতির প্রকৃত পার্থক্য
  • কেন merge শেয়ার্ড ব্রাঞ্চে সবসময় নিরাপদ কিন্তু rebase নয় — এবং এটি কীভাবে চূড়ান্ত সিদ্ধান্ত নির্ধারণ করে
  • বাস্তব টিমগুলো কীভাবে rebase ও merge একসাথে ব্যবহার করে (locally rebase, then merge via PR)
  • Python দিয়ে একটি সিদ্ধান্ত-নেওয়ার ফাংশন লেখা যা কখনোই শেয়ার্ড ব্রাঞ্চে rebase সুপারিশ করবে না

১ · হিস্ট্রির আকৃতি — সৎ কিন্তু জটিল, বনাম পরিষ্কার কিন্তু তথ্য-হারানো

M8/L35-এর mergeMergeদুটো ব্রাঞ্চের ইতিহাস একসাথে করে একটি two-parent merge commit তৈরি করা। এবং M8/L37-এর rebaseRebaseএকটি ব্রাঞ্চের commit গুলো অন্য ব্রাঞ্চের ওপর replay করে একটি লিনিয়ার হিস্ট্রি তৈরি করা। দুটোই একই লক্ষ্যে পৌঁছায় (একটি ব্রাঞ্চের পরিবর্তন আরেকটিতে আনা) কিন্তু ফলাফলের হিস্ট্রির আকৃতি সম্পূর্ণ ভিন্ন। Merge সত্যিকারের, প্রকৃত ইতিহাস সংরক্ষণ করে — কখন ব্রাঞ্চ ডাইভার্জ করেছিল, কখন আবার একসাথে হয়েছিল, সবকিছু merge commit-এর দুটো parent-এর মাধ্যমে দৃশ্যমান থাকে — একটি বেশি "সৎ" কিন্তু ঘন ঘন মার্জ হলে সম্ভাব্যভাবে বেশি এলোমেলো হিস্ট্রি। Rebase একটি পরিষ্কার, লিনিয়ার হিস্ট্রি তৈরি করে — আলাদাভাবে পড়া/অনুসরণ করা সহজ, কিন্তু কোন commit মূলত কোন ব্রাঞ্চের অংশ ছিল বা ঠিক কখন ডাইভার্জ হয়েছিল — সেই তথ্য হারিয়ে যায়। এটি একটি সৎ, বাস্তব ট্রেড-অফ — rebase-কে "একতরফাভাবে ভালো" হিসেবে দেখানো ঠিক নয়।

২ · নিরাপত্তা — সিদ্ধান্তের সবচেয়ে গুরুত্বপূর্ণ মানদণ্ড

L37-এর সোনালী নিয়মের সরাসরি পুনরাবৃত্তি: merge সবসময় শেয়ার্ড/পুশ-করা ব্রাঞ্চে ব্যবহার করা নিরাপদ — কারণ এটি কখনো বিদ্যমান commit হ্যাশ পুনর্লিখন করে না। বিপরীতে, rebase শুধুমাত্র লোকাল, এখনো-অন্য-কারো-সাথে-শেয়ার-না-করা commit-এ ব্যবহার করা উচিত — কারণ rebase নতুন হ্যাশ তৈরি করে যা শেয়ার-করা কপিগুলোর সাথে ডাইভার্জ করাবে।

৩ · বাস্তব প্র্যাকটিস — দুটোর সুবিধাই একসাথে

অনেক টিম (একমাত্র সঠিক পদ্ধতি নয়, কিন্তু একটি সাধারণ, বাস্তব কনভেনশন) দুটোকেই একসাথে ব্যবহার করে: একটি লোকাল feature ব্রাঞ্চ, PR (M9/L40) খোলার আগে, rebase দিয়ে পরিষ্কার/স্কোয়াশ করা হয় (এখনো শেয়ার হয়নি, তাই নিরাপদ) — তারপর সেই পরিষ্কার ব্রাঞ্চটি PR প্ল্যাটফর্মের "merge" বাটনের মাধ্যমে শেয়ার্ড main/develop ব্রাঞ্চে ইন্টিগ্রেট করা হয় (merge, তাই শেয়ার্ড ব্রাঞ্চে নিরাপদ)। এভাবে rebase-এর পরিষ্কার-হিস্ট্রি সুবিধা এবং merge-এর শেয়ার্ড-ব্রাঞ্চ-নিরাপত্তা — দুটোই একসাথে পাওয়া যায়।

ব্রাঞ্চ কি ইতিমধ্যে শেয়ার/পুশ করা হয়েছে? হ্যাঁ না শেয়ার্ড ব্রাঞ্চ→ সবসময় Merge (Golden Rule) লোকাল ব্রাঞ্চ→ clean history চাইলে Rebase, নাহলে Merge
শেয়ার্ড/পুশ-করা ব্রাঞ্চে rebase কখনোই সুপারিশ করা হয় না — clean-history পছন্দ যাই হোক না কেন, নিরাপত্তা সবসময় অগ্রাধিকার পায়।

নিচের কোড সেলে এই ঠিক সিদ্ধান্ত-লজিকটি একটি ফাংশন হিসেবে লেখা হয়েছে, এবং কয়েকটি বাস্তবসম্মত পরিস্থিতিতে পরীক্ষা করা হয়েছে — বিশেষভাবে এমন একটি কেসসহ যেখানে কেউ ভুলবশত শেয়ার্ড ব্রাঞ্চে rebase করতে চাইছে।

Python
# merge-বনাম-rebase সিদ্ধান্ত লজিক -- L37-এর সোনালী নিয়মকে সরাসরি কোডে প্রয়োগ করা হয়েছে।

def recommend_integration_strategy(is_branch_shared, wants_clean_linear_history):
    if is_branch_shared:
        warning = None
        if wants_clean_linear_history:
            warning = (
                "সতর্কতা: এই ব্রাঞ্চ ইতিমধ্যে শেয়ার/পুশ করা হয়েছে -- rebase করলে নতুন commit "
                "hash তৈরি হবে, যা অন্য যাদের কাছে পুরনো commit আছে তাদের সাথে ইতিহাস ডাইভার্জ "
                "করাবে (Golden Rule of Rebasing)। তাই clean history চাইলেও rebase নয়।"
            )
        return {
            "strategy": "merge",
            "reason": "শেয়ার্ড ব্রাঞ্চে rebase কখনোই নিরাপদ নয় -- Golden Rule অনুযায়ী সবসময় merge।",
            "warning": warning,
        }

    if wants_clean_linear_history:
        return {
            "strategy": "rebase",
            "reason": "লোকাল, এখনো-শেয়ার-না-করা ব্রাঞ্চ -- rebase নিরাপদ এবং clean, linear history দেয়।",
            "warning": None,
        }

    return {
        "strategy": "merge",
        "reason": "লোকাল ব্রাঞ্চ, কিন্তু clean history-এর বিশেষ প্রয়োজন নেই -- সহজ merge যথেষ্ট।",
        "warning": None,
    }


scenarios = [
    ("শেয়ার্ড main ব্রাঞ্চ, clean history চাই",           True,  True),
    ("শেয়ার্ড main ব্রাঞ্চ, clean history চাই না",         True,  False),
    ("লোকাল feature ব্রাঞ্চ, PR-এর আগে পরিষ্কার করতে চাই", False, True),
    ("লোকাল feature ব্রাঞ্চ, স্বাভাবিক merge-ই যথেষ্ট",     False, False),
    ("ভুলবশত শেয়ার্ড ব্রাঞ্চে rebase করতে চাওয়া",           True,  True),
]

for label, is_shared, wants_clean in scenarios:
    result = recommend_integration_strategy(is_shared, wants_clean)
    print(f"[{label}]")
    print("  strategy:", result["strategy"])
    print("  reason  :", result["reason"])
    if result["warning"]:
        print("  ⚠", result["warning"])
    print()

# যাচাই: শেয়ার্ড ব্রাঞ্চে সবসময় merge সুপারিশ হয়, clean-history পছন্দ যাই হোক না কেন
all_shared_get_merge = all(
    recommend_integration_strategy(True, wants)["strategy"] == "merge"
    for wants in (True, False)
)
print("শেয়ার্ড ব্রাঞ্চে সবসময় merge সুপারিশ হয়?", all_shared_get_merge)

    
লক্ষ্য করুন সবচেয়ে গুরুত্বপূর্ণ পাঁচ নম্বর দৃশ্য: is_branch_shared=True এবং wants_clean_linear_history=True — অর্থাৎ ব্যবহারকারী স্পষ্টভাবে rebase চাইছে (পরিষ্কার হিস্ট্রির জন্য) — কিন্তু ফাংশনটি তবুও "merge"-ই ফেরত দেয়, সাথে একটি স্পষ্ট সতর্কবার্তা। এটিই দেখায় নিরাপত্তা (branch শেয়ার্ড কিনা) সবসময় ব্যবহারকারীর পছন্দের (clean history) ওপর অগ্রাধিকার পায় — কখনো উল্টো নয়।
মূল কথা · Key takeaway

Merge ও rebase-এর মধ্যে কোনোটাই "সবসময় সঠিক" নয় — এটি একটি সৎ ট্রেড-অফ (হিস্ট্রির সততা/সরলতা বনাম পরিষ্কার/লিনিয়ার আকৃতি)। কিন্তু নিরাপত্তার প্রশ্নে কোনো ট্রেড-অফ নেই: শেয়ার্ড ব্রাঞ্চে rebase কখনোই করবেন না। পরের মডিউলে (M9) আমরা দেখব কীভাবে রিমোট, পুল রিকোয়েস্ট, ও Git Flow-এর মতো ওয়ার্কফ্লো এই দুটো টুলকেই একটি সংগঠিত টিম-প্র্যাকটিসে একত্রিত করে।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।

প্র ০১ কেন merge শেয়ার্ড ব্রাঞ্চে সবসময় নিরাপদ, কিন্তু rebase নয় — মূল কারিগরি পার্থক্যটা ঠিক কী?

Merge কখনো বিদ্যমান commit-এর হ্যাশ পরিবর্তন/পুনর্লিখন করে না — এটি শুধু একটি নতুন merge commit যোগ করে, পুরনো সব commit অপরিবর্তিত থাকে। Rebase বিপরীতভাবে বিদ্যমান commit-গুলোকে নতুন parent-সহ নতুন commit দিয়ে "প্রতিস্থাপন" করে — যাদের কাছে পুরনো হ্যাশগুলো আছে তাদের হিস্ট্রির সাথে এই নতুন হ্যাশ মেলে না।

প্র ০২ "লোকাল ব্রাঞ্চে rebase, তারপর PR-এ merge" — এই দুটো একসাথে ব্যবহারের সুবিধা কী?

Rebase অংশটি (PR খোলার আগে, লোকালি) পরিষ্কার, রিভিউযোগ্য একটি লিনিয়ার commit হিস্ট্রি তৈরি করে — যা কোড রিভিউ (M11/L52) সহজ করে তোলে। Merge অংশটি (PR-এর merge বাটন) শেয়ার্ড ব্রাঞ্চে হ্যাশ পুনর্লিখনের ঝুঁকি ছাড়াই ইন্টিগ্রেশন সম্পন্ন করে। ফলাফল: পরিষ্কার হিস্ট্রি এবং শেয়ার্ড-ব্রাঞ্চ নিরাপত্তা — দুটো সুবিধাই একসাথে, কোনো আপস ছাড়াই।

প্র ০৩ কোড সেলে "শেয়ার্ড main ব্রাঞ্চ, clean history চাই না" পরিস্থিতিতেও কেন warning ফিল্ড None রাখা হয়েছে, অথচ strategy তবুও "merge"?

কারণ এখানে ব্যবহারকারী rebase চাইছেই না (wants_clean_linear_history=False) — সে ইতিমধ্যে merge-ই চাচ্ছে, তাই কোনো "rebase করবেন না" সতর্কবার্তার প্রয়োজন নেই; strategy স্বাভাবিকভাবেই merge। warning শুধুমাত্র তখনই সেট করা হয় যখন ব্যবহারকারীর ইচ্ছা (rebase/clean history) আর নিরাপদ সিদ্ধান্তের (merge) মধ্যে সরাসরি সংঘর্ষ হয় — যাতে ব্যবহারকারী বুঝতে পারে কেন তার পছন্দ অগ্রাহ্য করা হলো।

অনুশীলন

  1. চিন্তা করুন: আপনার টিমের একটি release ব্রাঞ্চ, যেখানে একাধিক ডেভেলপার নিয়মিত পুশ করছে, প্রায়শই মার্জ কনফ্লিক্ট (M8/L36) নিয়ে বিরক্তিকর হয়ে উঠছে। কেউ প্রস্তাব দিলো "চলুন release ব্রাঞ্চে সবসময় rebase ব্যবহার করি, তাহলে হিস্ট্রি পরিষ্কার থাকবে" — এই প্রস্তাব কেন সমস্যাজনক, এবং আপনি কী বিকল্প প্রস্তাব দেবেন?

    যেহেতু release ইতিমধ্যে একাধিক ডেভেলপারের মধ্যে শেয়ার্ড, সেখানে rebase করলে সোনালী নিয়ম ভঙ্গ হবে — প্রতিটি rebase অন্য ডেভেলপারদের লোকাল কপির সাথে হিস্ট্রি ডাইভার্জ করাবে। ভালো বিকল্প: প্রতিটি ডেভেলপার তাদের নিজের লোকাল feature ব্রাঞ্চে rebase করে পরিষ্কার করুক (এখনো শেয়ার হয়নি), তারপর সেই পরিষ্কার ব্রাঞ্চ merge/PR দিয়ে release-এ আনুক — কনফ্লিক্ট কমবে ছোট, ঘন ঘন ইন্টিগ্রেশনের কারণে, শেয়ার্ড ব্রাঞ্চে rebase না করেই।

  2. পরীক্ষা করুন: উপরের কোড সেলে scenarios লিস্টে আরেকটি নতুন দৃশ্য যোগ করুন — ("লোকাল hotfix ব্রাঞ্চ, দ্রুত merge করতে চাই", False, False) — এবং Run চেপে দেখুন ফলাফল প্রত্যাশিত কিনা।

    যেহেতু is_branch_shared=False এবং wants_clean_linear_history=False, ফাংশনটি তৃতীয় শাখায় পড়বে এবং "merge" ফেরত দেবে, reason: "লোকাল ব্রাঞ্চ, কিন্তু clean history-এর বিশেষ প্রয়োজন নেই -- সহজ merge যথেষ্ট।" — কোনো warning ছাড়াই, কারণ এখানে কোনো নিরাপত্তা-সংঘর্ষ নেই।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

আগের পাঠ
Git Rebase — ধারণা ও ইন্টারঅ্যাক্টিভ রিবেস