git init, add, commit — থ্রি-ট্রি আর্কিটেকচার
এই পাঠে যা শিখবেন
- Git-এর থ্রি-ট্রি মডেল — Working Directory, Staging Area, Repository — প্রতিটির নির্দিষ্ট ভূমিকা
- Staging area কেন দরকার — শুধু একটি বাড়তি ধাপ নয়, একটি genuine, প্র্যাকটিক্যাল সুবিধা
git init,git add,git commitঠিক কোন ট্রি বদলায়, কীভাবে- একটি বাস্তব
Repoসিমুলেশন বানিয়ে edit → stage → commit → edit-again ফ্লো হাতে-কলমে ট্রেস করা
১ · Git-এর থ্রি-ট্রি মডেল
এখান থেকে M7-M9-এর প্রতিটি পাঠের ভিত্তি — Git-এর তিনটি "ট্রি" (state/স্ন্যাপশট) ঠিক কী, একদম নির্দিষ্টভাবে
জানা প্রয়োজন। বাস্তব git বাইনারি এই ব্রাউজার-স্যান্ডবক্সে (Pyodide) চলে না — তাই এই পাঠ থেকে
শুরু করে M7-M9 জুড়ে আমরা Git-এর অভ্যন্তরীণ অবজেক্ট মডেল Python data structure দিয়ে from-scratch
সিমুলেট করব, বাস্তব git-এর আচরণের সাথে নিখুঁতভাবে মিলিয়ে — কখনোই real git "রান" করার
দাবি না করে।
আপনি এখন যে ফাইলগুলো এডিট করছেন, ডিস্কে থাকা তাদের বর্তমান অবস্থা।
git add-এর মাধ্যমে চিহ্নিত পরিবর্তনের একটি স্ন্যাপশট। (Index)পরের কমিটে যেতে প্রস্তুত হিসেবে
git add-এর মাধ্যমে বেছে নেওয়া পরিবর্তনের একটি ইন্টারমিডিয়েট স্ন্যাপশট।স্থায়ীভাবে সেভ করা স্ন্যাপশট (কমিট) — প্রতিটি কমিট তার parent কমিটকে পয়েন্ট করে (L01-এর প্রিভিউর সরাসরি পুনর্ব্যবহার)।
Staging Area-এর genuine উদ্দেশ্যটি স্পষ্টভাবে বলা দরকার — এটি নিছক একটি বাড়তি, বিরক্তিকর ধাপ নয়। এটি আপনাকে সুনির্দিষ্টভাবে বেছে নিতে দেয় ঠিক কোন পরিবর্তনগুলো পরের কমিটে যাবে, এমনকি যদি আপনার Working Directory-তে একসাথে একাধিক unrelated পরিবর্তন থেকে থাকে — যেমন একটি বাগ-ফিক্সের পরিবর্তন শুধু stage করে একটি focused কমিট বানানো, আর অসম্পর্কিত work-in-progress পরিবর্তন unstaged রেখে দেওয়া পরের কমিটের জন্য।
২ · তিনটি মূল কমান্ড, নির্দিষ্টভাবে
এই তিনটি কমান্ড এই থ্রি-ট্রি মডেলের সাথে একদম সরাসরি মিলিয়ে বলা যায়:
git initএকটি নতুন, খালি রিপোজিটরি তৈরি করে — একটি খালি "ট্রি ৩" (Repository) দিয়ে শুরু।
git add <file>ফাইলের বর্তমান অবস্থা Working Directory থেকে Staging Area-তে কপি করে।
git commitStaging Area-তে যা কিছু আছে তা নিয়ে একটি নতুন কমিট তৈরি করে, তার parent বসায় আগের HEAD-এ, HEAD-কে নতুন কমিটে সরায়।
git add কপি করে Working Directory থেকে Staging Area-তে। git commit Staging Area-র একটি COPY নিয়ে Repository-তে একটি নতুন, স্থায়ী কমিট বানায় — এবং তারপর Staging Area খালি করে দেয়।৩ · একটি বাস্তব Repo সিমুলেশন
নিচের কোড সেলে Git-এর থ্রি-ট্রি মডেলের একটি genuine, runnable Python সিমুলেশন — তিনটি স্পষ্ট dict
(working_directory, staging_area, commits) দিয়ে। এটি real git-এর
অভ্যন্তরীণ অ্যালগরিদমের একটি from-scratch সিমুলেশন, বাস্তব git-এর আচরণের সাথে নিখুঁতভাবে মিলিয়ে — কোনো real
git বাইনারি এখানে চলছে না।
# Git-এর থ্রি-ট্রি অভ্যন্তরীণ মডেলের from-scratch সিমুলেশন -- বাস্তব git বাইনারি নেই,
# তাই working_directory, staging_area, commits -- তিনটি dict দিয়ে সরাসরি বাস্তবায়ন।
def make_repo():
return {
"working_directory": {}, # filename -> content
"staging_area": {}, # filename -> content (শুরুতে খালি)
"commits": {}, # hash -> {message, snapshot, parent}
"head": None, # সর্বশেষ কমিটের hash, শুরুতে কোনো কমিট নেই
}
def git_add(repo, filename):
# working directory থেকে ফাইলের বর্তমান কনটেন্ট staging area-তে কপি করা
repo["staging_area"][filename] = repo["working_directory"][filename]
def git_commit(repo, message):
commit_hash = f"c{len(repo['commits']) + 1}"
snapshot = dict(repo["staging_area"]) # staging area-র একটি COPY, রেফারেন্স নয়
repo["commits"][commit_hash] = {
"message": message,
"snapshot": snapshot,
"parent": repo["head"],
}
repo["head"] = commit_hash
repo["staging_area"] = {} # বাস্তব git-এর মতোই কমিটের পর staging area খালি হয়ে যায়
return commit_hash
def print_trees(repo, label):
print(f"-- {label} --")
print("Working Directory:", repo["working_directory"])
print("Staging Area: ", repo["staging_area"])
if repo["head"]:
head_snapshot = repo["commits"][repo["head"]]["snapshot"]
print("HEAD কমিট: ", repo["head"], "->", head_snapshot)
else:
print("HEAD কমিট: (এখনও কোনো কমিট নেই)")
print()
repo = make_repo() # git init
# ধাপ ১: readme.txt ফাইল এডিট করা (শুধু working directory-তে)
repo["working_directory"]["readme.txt"] = "Hello v1"
print_trees(repo, "ধাপ ১: readme.txt এডিট করা হলো (working directory)")
# ধাপ ২: স্টেজ করা
git_add(repo, "readme.txt")
print_trees(repo, "ধাপ ২: git add readme.txt")
# ধাপ ৩: কমিট করা
c1 = git_commit(repo, "প্রথম কমিট: readme যোগ করা হলো")
print_trees(repo, f"ধাপ ৩: git commit -> {c1}")
# ধাপ ৪: আবার এডিট করা, কিন্তু এবার স্টেজ না করা
repo["working_directory"]["readme.txt"] = "Hello v2 (staged করা হয়নি)"
print_trees(repo, "ধাপ ৪: readme.txt আবার এডিট, কিন্তু git add করা হয়নি")
"Hello v2 (staged করা হয়নি)"
আছে, কিন্তু c1 কমিটের snapshot এখনও "Hello v1"-ই দেখাচ্ছে। এটিই
git_add আর git_commit-এর আলাদা ভূমিকার সবচেয়ে concrete প্রমাণ — দ্বিতীয় এডিটটি
কখনো stage হয়নি, তাই সেটি কোনো কমিটেই যায়নি, যতক্ষণ না কেউ আবার git_add কল করে।
Git-এর প্রতিটি অপারেশন আসলে এই তিনটি ট্রির মধ্যে ডেটা কপি করার একটি নির্দিষ্ট নিয়ম। git add
Working Directory → Staging Area কপি করে; git commit Staging Area-র একটি COPY নিয়ে
Repository-তে একটি নতুন, স্থায়ী কমিট বানায়, তারপর Staging Area খালি করে দেয়। এই মডেলটিই M7-এর বাকি
পাঠগুলোর (L31-L33) এবং M8-M9-এর ব্রাঞ্চিং/মার্জিং/রিমোট আলোচনার ভিত্তি — এখান থেকে আমরা শুধু এই একই তিনটি
dict-এর ওপর নতুন কমান্ড/ফাংশন যোগ করে যাব।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১
কোড সেলের ধাপ ৪-এ, যদি এখনই আরেকবার git_commit(repo, "দ্বিতীয় কমিট") কল করা হয়, তাহলে নতুন কমিটের snapshot-এ কী থাকবে?
একটি খালি snapshot ({}) — কারণ ধাপ ৩-এর কমিটের পর Staging Area খালি হয়ে
গিয়েছিল, আর ধাপ ৪-এর এডিটটি কখনো git_add দিয়ে স্টেজ করা হয়নি। git_commit
সবসময় Staging Area-র বর্তমান অবস্থা থেকে স্ন্যাপশট নেয়, Working Directory থেকে সরাসরি
নয় — এটিই এই পাঠের সবচেয়ে গুরুত্বপূর্ণ, প্রায়ই ভুল বোঝা আচরণ।
প্র ০২
git_commit ফাংশনে snapshot = dict(repo["staging_area"]) লেখা হয়েছে, সরাসরি repo["staging_area"] রেফারেন্স রাখা হয়নি কেন?
কারণ যদি সরাসরি রেফারেন্স রাখা হতো, তাহলে পরে repo["staging_area"] = {} লাইনটি
একই dict object-কে খালি করে দিত, যেটি কমিটের snapshot হিসেবেও ব্যবহৃত হচ্ছিল —
ফলে পুরোনো কমিটের snapshot-ও খালি হয়ে যেত! dict(...) দিয়ে একটি স্বাধীন COPY বানানোয়,
Staging Area পরে খালি হলেও কমিটের snapshot-এ তার নিজস্ব, স্থায়ী কপি থেকে যায় — ঠিক real git যেভাবে
প্রতিটি কমিটকে immutable রাখে।
প্র ০৩
যদি ধাপ ৪-এর পরে আবার git_add(repo, "readme.txt") কল করা হয়, staging area-তে কী দেখা যাবে, এবং তারপর git_commit কল করলে নতুন কমিটের parent কী হবে?
git_add কল করলে staging area-তে {"readme.txt": "Hello v2 (staged করা হয়নি)"}
যোগ হবে (এখন এটি স্টেজড)। তারপর git_commit কল করলে একটি নতুন কমিট (c2) তৈরি
হবে, যার parent হবে c1 (কারণ repo["head"] তখন c1-ই
ছিল) — ঠিক L01-এর প্রিভিউ কোড সেলের মতোই, প্রতিটি নতুন কমিট আগের HEAD-কে parent হিসেবে ধরে রাখে।
অনুশীলন
-
চিন্তা করুন: একজন ডেভেলপার একটি বাগ-ফিক্স আর একটি অসম্পূর্ণ নতুন ফিচার — দুটোই একসাথে একই ফাইলে এডিট করে ফেলেছে, কিন্তু শুধু বাগ-ফিক্সটুকু এখনই কমিট করতে চায়। Staging area কীভাবে এই পরিস্থিতিতে ব্যবহারিকভাবে সাহায্য করে তা ব্যাখ্যা করুন।
Git-এ
git add -p-জাতীয় টুল দিয়ে একটি ফাইলের নির্দিষ্ট অংশ (hunk) বেছে stage করা যায় — শুধু বাগ-ফিক্সের লাইনগুলো staging area-তে যোগ করে কমিট করা যাবে, আর অসম্পূর্ণ ফিচারের লাইনগুলো Working Directory-তেই unstaged থেকে যাবে, পরে আলাদা কমিটের জন্য অপেক্ষা করবে। এই লেভেলের নিয়ন্ত্রণ সম্ভবই হতো না যদিgit commitসরাসরি Working Directory থেকে স্ন্যাপশট নিত। -
পরীক্ষা করুন: উপরের কোড সেলে ধাপ ৪-এর পরে একটি নতুন ফাইল
notes.txtworking directory-তে যোগ করুন, সেটিকেgit_addওgit_commitকরুন (নতুন মেসেজ দিয়ে) — নতুন কমিটের snapshot-এ কি শুধুnotes.txtথাকবে, নাকিreadme.txt-ও (v1 অবস্থায়) থাকবে? Run চেপে যাচাই করুন।নতুন কমিটের snapshot-এ শুধু
notes.txtথাকবে — কারণ ধাপ ৩-এর কমিটের পর staging area খালি হয়ে গিয়েছিল, আরreadme.txtকখনো আবার stage করা হয়নি। প্রতিটি কমিটের snapshot শুধু সেই মুহূর্তে staging area-তে যা ছিল তা-ই ধারণ করে — আগের কমিটের পুরো ফাইল-সেট নয়, এটি এই পাঠের সিমুলেশনের একটি সরলীকরণ যা বাস্তব git-এর "প্রতিটি কমিট একটি সম্পূর্ণ ট্রি স্ন্যাপশট" আচরণকে দেখানোর জন্য যথেষ্ট নয় বলে মনে রাখা জরুরি -- L31-এgit_status/git_diffযোগ করার সময় এই সীমাবদ্ধতাটি আরও স্পষ্ট হবে।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
-
পরবর্তী পাঠ · git status, diff ও log L31
এই পাঠের
Repoমডেল সরাসরি সম্প্রসারিত হবে — তিনটি ট্রি তুলনা করে status ও diff বের করা। - সফটওয়্যার ইঞ্জিনিয়ারিং কী ও Git কেন গুরুত্বপূর্ণ L01 L01-এর সরলীকৃত কমিট-হিস্ট্রি প্রিভিউ এখন এই পাঠে সম্পূর্ণ, নির্ভুল থ্রি-ট্রি মডেলে পরিণত হলো।
-
কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৮টি পাঠ
এই
Repoমডেলই M7-M9 জুড়ে ব্রাঞ্চিং, মার্জিং, রিবেস, ও রিমোটের ভিত্তি হয়ে থাকবে।