পাঠ ৪০ · ৫৮-এর মধ্যে · মডিউল ৯
Home / Courses / Software Engineering Principles & Git / পুল রিকোয়েস্ট

পুল রিকোয়েস্ট ও কোড রিভিউ ওয়ার্কফ্লো

Pull requests & code review workflow
৮ মিনিট পড়া মধ্যম · Intermediate Python কোডসহ সম্পূর্ণ বাংলায়

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

  • Pull Request আসলে কী, এবং কেন এটি raw git কমান্ড নয় বরং একটি প্ল্যাটফর্ম ফিচার
  • স্ট্যান্ডার্ড PR ওয়ার্কফ্লোর পাঁচটি ধাপ, M7-M9 এর এখন-পর্যন্ত শেখা সবকিছুকে একসাথে যুক্ত করে
  • PR কেন কোড রিভিউ ও CI-এর জন্য একটি গুরুত্বপূর্ণ চেকপয়েন্ট তৈরি করে
  • Python দিয়ে একটি বাস্তবসম্মত approval-gate (can_merge()) লজিক লেখা ও ধাপে ধাপে যাচাই করা

১ · Pull Request কী

Pull Request (PR)Pull Requestএকটি ব্রাঞ্চের পরিবর্তন আরেকটিতে merge করার জন্য একটি হোস্টিং প্ল্যাটফর্মে খোলা রিকোয়েস্ট — কিছু প্ল্যাটফর্মে "Merge Request" নামেও পরিচিত। হলো একটি ব্রাঞ্চ (প্রায়ই একটি feature ব্রাঞ্চ, M8-এর ব্রাঞ্চিং উপাদানের সরাসরি প্রয়োগ) আরেকটিতে (প্রায়ই main/শেয়ার্ড ব্রাঞ্চ) merge করার একটি রিকোয়েস্ট, যা GitHub/GitLab-এর মতো একটি হোস্টিং প্ল্যাটফর্মে খোলা হয়। একটি গুরুত্বপূর্ণ বিষয় স্পষ্ট করা দরকার: PR নিজে একটি raw git কমান্ড নয় — এটি git-এর প্রকৃত ব্রাঞ্চ/মার্জ মেকানিক্সের ওপর প্ল্যাটফর্মের তৈরি করা একটি ফিচার।

২ · স্ট্যান্ডার্ড PR ওয়ার্কফ্লো

M7-M9-এ এখন পর্যন্ত শেখা প্রায় সবকিছুকে এখানে একসাথে যুক্ত করা যাক:

১. Feature ব্রাঞ্চ তৈরি ও commit করা (M8) ২. ব্রাঞ্চ remote-এ push করা (L39) ৩. প্ল্যাটফর্মে Pull Request খোলা (feature → main) ৪. টিম রিভিউ করে — approve / changes requested ৫. Approve হলে merge (কোড রিভিউ + CI চেক-সহ)
PR M7-M9-এর প্রায় সবকিছুকে একটি বাস্তব টিম-ওয়ার্কফ্লোতে যুক্ত করে — ব্রাঞ্চিং (M8), রিমোট (L39), এবং শেষে merge (M8/L35), সাথে একটি নতুন রিভিউ-চেকপয়েন্ট যোগ করে।

৩ · কেন PR গুরুত্বপূর্ণ

PR একটি প্রাকৃতিক চেকপয়েন্ট তৈরি করে কোড রিভিউর জন্য (M11/L52-এ বিস্তারিত দেখব) এবং স্বয়ংক্রিয় CI চেক (M12/L53-এ দেখব) চালানোর জন্য — শেয়ার্ড ব্রাঞ্চে পৌঁছানোর আগেই। এটি একটি গুরুত্বপূর্ণ কোয়ালিটি-গেট যা সরাসরি main-এ git push করাতে থাকে না।

নিচের কোড সেলে একটি বাস্তবসম্মত PullRequest ক্লাস লেখা হয়েছে, যার can_merge() মেথড দুটো শর্ত একসাথে যাচাই করে — অন্তত একটি approval, এবং কোনো outstanding changes-requested রিভিউ নেই। লক্ষ্য করুন প্রতিটি রিভিউয়ারের সর্বশেষ সিদ্ধান্তই টিকে থাকে — কেউ প্রথমে changes requested দিয়ে পরে approve করলে তার আগের সিদ্ধান্ত আর গণনায় আসে না।

Python
# PullRequest ও approval-gate লজিক -- এটি সাধারণ, real Python OOP (git-এর সিমুলেশন নয়,
# কারণ PR নিজেই কোনো git অভ্যন্তরীণ অবজেক্ট নয়, বরং একটি প্ল্যাটফর্ম-লেভেল ধারণা)।

class PullRequest:
    def __init__(self, source_branch, target_branch):
        self.source_branch = source_branch
        self.target_branch = target_branch
        self.status = "open"
        self.reviews = []  # [{"reviewer":..., "decision":..., "comment":...}]

    def add_review(self, reviewer, decision, comment):
        self.reviews.append({"reviewer": reviewer, "decision": decision, "comment": comment})

    def can_merge(self):
        latest_by_reviewer = {}
        for r in self.reviews:
            latest_by_reviewer[r["reviewer"]] = r["decision"]  # শেষেরটাই টিকে থাকে

        has_approval = "approved" in latest_by_reviewer.values()
        has_outstanding_changes_requested = "changes_requested" in latest_by_reviewer.values()
        return has_approval and not has_outstanding_changes_requested

    def merge(self):
        if not self.can_merge():
            raise ValueError("can_merge() False -- merge করা যাবে না")
        self.status = "merged"


pr = PullRequest("feature/login-page", "main")

pr.add_review("অনিতা", "changes_requested", "পাসওয়ার্ড ভ্যালিডেশন মিসিং")
print("অনিতা 'changes_requested' দেওয়ার পর          can_merge():", pr.can_merge())

pr.add_review("রাহুল", "approved", "বাকি সব ঠিক আছে")
print("রাহুল 'approved' দেওয়ার পরও                can_merge():", pr.can_merge(),
      "(অনিতার changes_requested এখনো outstanding)")

pr.add_review("অনিতা", "approved", "ভ্যালিডেশন যোগ হয়েছে, এখন ঠিক আছে")
print("অনিতা নিজের রিভিউ বদলে 'approved' দেওয়ার পর  can_merge():", pr.can_merge())

pr.merge()
print("\nPR status:", pr.status)

print("\nমোট রিভিউ এন্ট্রি:", len(pr.reviews))
latest = {}
for r in pr.reviews:
    latest[r["reviewer"]] = r["decision"]
print("প্রতিটি রিভিউয়ারের সর্বশেষ সিদ্ধান্ত:", latest)

    
তিনটি ধাপের পরিবর্তন খেয়াল করুন। ধাপ ১: শুধু একটি changes-requested রিভিউ — কোনো approval নেই, তাই can_merge() False। ধাপ ২: রাহুল approve করলেন, এখন একটি approval আছে — কিন্তু অনিতার changes-requested এখনো outstanding, তাই এখনো can_merge() False (দুটো শর্তই একসাথে লাগে)। ধাপ ৩: অনিতা নিজের রিভিউ বদলে approve করলেন — এখন কোনো outstanding changes-requested নেই এবং অন্তত একটি approval আছে, তাই can_merge() True হয়ে merge সম্ভব হয়।
মূল কথা · Key takeaway

PR হলো git-এর ব্রাঞ্চ/মার্জ মেকানিক্সের ওপর তৈরি একটি প্ল্যাটফর্ম ফিচার, যা কোড main-এ পৌঁছানোর আগে রিভিউ ও যাচাইয়ের একটি প্রাকৃতিক চেকপয়েন্ট তৈরি করে। একটি ভালো approval-gate শুধু "কেউ approve করেছে কিনা" দেখে না — এটি নিশ্চিত করে কোনো outstanding আপত্তিও নেই, এবং প্রতিটি রিভিউয়ারের সর্বশেষ সিদ্ধান্তকেই গুরুত্ব দেয়। পরের পাঠে (L41) আমরা দেখব কীভাবে Git Flow-এর মতো একটি সংগঠিত ব্রাঞ্চিং স্ট্র্যাটেজি PR-ভিত্তিক ইন্টিগ্রেশনের সাথে মিলিয়ে পুরো একটি রিলিজ-প্রক্রিয়া গঠন করে।

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

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

প্র ০১ PR কি git-এর নিজস্ব কমান্ড, নাকি অন্য কিছু? এটি স্পষ্ট করে ব্যাখ্যা করুন।

PR git-এর নিজস্ব কমান্ড নয় — এটি একটি প্ল্যাটফর্ম (GitHub, GitLab, ইত্যাদি) ফিচার, যা git-এর প্রকৃত ব্রাঞ্চ ও মার্জ মেকানিক্সের ওপর তৈরি। যখন একটি PR merge করা হয়, প্ল্যাটফর্ম তার নিজের "merge" বাটনের মাধ্যমে ভেতরে ভেতরে একটি প্রকৃত git merge (অথবা squash-merge) চালায় — কিন্তু PR-এর রিভিউ/স্ট্যাটাস ট্র্যাকিং সিস্টেমটি নিজে raw git-এর অংশ নয়।

প্র ০২ উপরের কোড সেলে রাহুলের approval থাকা সত্ত্বেও দ্বিতীয় ধাপে can_merge() False কেন হলো?

কারণ can_merge()-এর নিয়ম হলো "AND" লজিক — অন্তত একটি approval এবং কোনো outstanding changes-requested নেই। রাহুলের approval প্রথম শর্তটি পূরণ করলেও, অনিতার changes-requested রিভিউ তখনও তার সর্বশেষ (এবং একমাত্র) সিদ্ধান্ত ছিল — অর্থাৎ দ্বিতীয় শর্তটি ব্যর্থ হয়। শুধু কোনো একজন approve করলেই যথেষ্ট নয়, যদি অন্য কারো আপত্তি এখনো সমাধান না হয়ে থাকে।

প্র ০৩ সরাসরি git push দিয়ে main-এ কোড পাঠানো আর PR-এর মাধ্যমে merge করার মধ্যে গুণগত পার্থক্য কী?

সরাসরি push-এ কোনো বাধ্যতামূলক চেকপয়েন্ট নেই — কোড সাথে সাথে শেয়ার্ড ব্রাঞ্চে চলে যায়। PR workflow-এ কোড প্রথমে একটি "প্রস্তাব" অবস্থায় থাকে — অন্য টিম সদস্যরা রিভিউ করতে পারে, স্বয়ংক্রিয় CI চেক চলতে পারে, এবং merge শুধুমাত্র can_merge()-এর মতো একটি স্পষ্ট নিয়ম পূরণ হলেই ঘটে। এটি ভুল/নিম্নমানের কোড শেয়ার্ড ব্রাঞ্চে পৌঁছানোর সম্ভাবনা উল্লেখযোগ্যভাবে কমায়।

অনুশীলন

  1. চিন্তা করুন: যদি একটি PR-এ তিনজন রিভিউয়ার থাকে, এবং দুজন approve করেন কিন্তু তৃতীয়জন কখনো কোনো রিভিউ-ই না দেন (নীরব থাকেন), তাহলে can_merge() কী ফেরত দেবে? এটি কি ন্যায্য আচরণ বলে মনে হয়?

    can_merge() True ফেরত দেবে, কারণ নীরব থাকা কোনো "changes_requested" এন্ট্রি তৈরি করে না — শুধু outstanding আপত্তি থাকলেই merge আটকায়, কারো রিভিউ না-দেওয়া নয়। এটি একটি বাস্তবসম্মত ডিজাইন সিদ্ধান্ত: টিমকে একজন অনুপস্থিত/ধীরগতির রিভিউয়ারের জন্য অসীম সময় অপেক্ষা করতে বাধ্য করা হয় না, যদিও বাস্তব প্ল্যাটফর্মগুলোতে প্রায়ই "প্রয়োজনীয় রিভিউয়ার" নির্দিষ্ট করার আলাদা সেটিং থাকে।

  2. পরীক্ষা করুন: একটি নতুন PullRequest("feature/x", "main") তৈরি করুন এবং শুরুতেই pr.merge() কল করে দেখুন কী হয় — তারপর ব্যাখ্যা করুন কেন।

    এটি একটি ValueError রেইজ করবে ("can_merge() False -- merge করা যাবে না"), কারণ কোনো রিভিউ এখনো যোগ করা হয়নি — latest_by_reviewer খালি, তাই has_approval False, এবং can_merge() False। এটি নিশ্চিত করে একটি PR কখনোই কোনো রিভিউ ছাড়া merge হতে পারবে না।

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

আগের পাঠ
রিমোট — clone, push, pull, fetch