شاخه: فقط یک اشارهگر
Branches: just movable pointers
اگر شاخه را «یک کپی از پروژه» ببینی، خیلی از رفتارهای Git عجیب میشود. این فصل با یک آزمایش ساده نشان میدهد شاخه در اصل فقط یک نام سبک است که به یک commit اشاره میکند.
“I created a branch, so Git must have copied the whole project.” It is a natural guess, but if true, branching would be expensive. Instead of guessing, let’s inspect the refs before and after a commit.
وقتی یک شاخه میسازیم، کپی کجاست؟When we create a branch, where is the copy?
در فصل قبل commitها را گرههای یک گراف دیدیم. حالا در مخزن کوچکی که فقط یک commit دارد، شاخهٔ فعلی را بررسی میکنیم و یک شاخهٔ تازه میسازیم. پیشبینی کن: آیا فایل تازهای ظاهر میشود؟ آیا شناسهٔ commit عوض میشود؟
The previous chapter showed commits as nodes in a graph. In a repository with one commit, we’ll inspect the current branch and create another. Predict first: will a new file appear? Will the commit ID change?
mkdir branch-pointer-lab
Set-Location branch-pointer-lab
git init -b main
git config user.name 'Nika Example'
git config user.email 'nika@example.test'
'v1' | Out-File -Encoding utf8 app.txt
git add app.txt
git commit -m "Add first version"
git rev-parse refs/heads/main
git branch experiment
git show-ref --heads
3a91c208ed61424d9fdd16f7998c378b52c04b6c refs/heads/experiment
3a91c208ed61424d9fdd16f7998c378b52c04b6c refs/heads/mainدر ویندوز PowerShell این نمونهٔ ساخت فایل کار میکند؛ در Bash میتوانی بهجایش printf 'v1\n' > app.txt بنویسی. دو شناسهٔ کنار refs/heads/main و refs/heads/experiment باید برابر باشند. هنوز commit تازهای نساختهایم؛ فقط یک نام دیگر برای همان نقطهٔ گراف ساخته شده است.
The file-creation line works in PowerShell; in Bash, use printf 'v1\n' > app.txt instead. The IDs beside refs/heads/main and refs/heads/experiment should match. No commit was created: Git added another name for the same graph position.
شاخه یک اشارهگر نامدار است که commit نوک شاخه را مشخص میکند. اشارهگر با ساخت شاخه جابهجا نمیشود؛ فعلاً فقط نام تازهای به همان commit اشاره میکند. فایلهای پروژه از قبل در شیءهای Git هستند؛ شاخهسازی آنها را دوباره کپی نمیکند.
A branch is a named ref that identifies the tip commit. Creating it does not move the ref; for now, a new name points to the same commit. The project files already live in Git objects—branch creation does not copy them again.
commit بعدی، کدام نام را جلو میبرد؟Which name moves when we make the next commit?
تا اینجا هر دو شاخه هممکاناند. حالا روی experiment میرویم و تغییر کوچکی commit میکنیم. سؤال دقیق این است: آیا Git شاخهٔ دیگر را هم جلو میبرد، یا فقط اشارهگر شاخهٔ جاری را؟
So far both branches occupy the same point. Switch to experiment and commit a small change. The precise question is: does Git advance both names, or only the ref for the current branch?
git switch experiment
git symbolic-ref HEAD
'v2 from experiment' | Out-File -Encoding utf8 app.txt
git add app.txt
git commit -m "Try alternate version"
git show-ref --heads
git log --oneline --graph --decorate --all
b7c4e11 (HEAD -> experiment) Try alternate version
3a91c20 (main) Add first versionبعد از switch، خروجی git symbolic-ref HEAD باید refs/heads/experiment باشد. بعد از commit، شناسهٔ آزمایش تغییر میکند و main روی C1 میماند. commit جدید C2 فرزند C1 است؛ «جدا شدن شاخهها» یعنی دو اشارهگر اکنون نوکهای متفاوتی از گراف را نام میبرند.
After switching, git symbolic-ref HEAD should print refs/heads/experiment. After the commit, experiment’s ID changes while main stays at C1. C2 is a child of C1; the branches have diverged in the sense that their refs now name different graph tips.
| کارAction | چه چیزی تغییر میکند؟What changes? | چه چیزی خودبهخود تغییر نمیکند؟What does not change automatically? |
|---|---|---|
git branch experiment | اشارهگر تازه به commit جاری میرسد.A new ref points to the current commit. | فایلها و commitها کپی نمیشوند.Files and commits are not copied. |
git switch experiment | HEAD شاخهٔ جاری را عوض میکند؛ فایلهای ردیابیشده با مقصد هماهنگ میشوند.HEAD changes its current branch; tracked files update to match the target. | خود commit یا اشارهگرهای شاخههای دیگر جابهجا نمیشوند.The commit and other branch refs do not move. |
git commit | commit تازه ساخته و اشارهگر شاخهٔ جاری به آن جلو میرود.A new commit is created and the current branch ref advances to it. | شاخههای دیگر روی نوک قبلی میمانند.Other branches remain at their previous tips. |
HEAD دقیقاً کجا ایستاده؟Where exactly is HEAD pointing?
سه نام را از هم جدا کنیم: commitها عکس فوریهای گرافاند؛ شاخهها اشارهگرهایی هستند که نوکهای موردعلاقه را نام میبرند؛ HEAD نشان میدهد checkout فعلی از کدام اشارهگر یا commit شروع شده است. معمولاً HEAD به شاخه اشاره میکند، نه اینکه خودش «شاخه» باشد.
Keep three names distinct: commits are snapshots in the graph; branches are refs naming tips; HEAD identifies the ref or commit currently checked out. Normally HEAD points to a branch—it is not itself the branch.
git symbolic-ref HEAD git rev-parse HEAD git rev-parse refs/heads/main git show-ref --heads
symbolic-ref نام کامل شاخهٔ متصل به HEAD را میدهد؛ rev-parse شناسهٔ commit حلشده را. در حالت عادی، دو فرمان دوم برای شاخهٔ فعلی یک شناسه میدهند. برای دیدن اشارهگرها از show-ref استفاده کن، نه اینکه فرض کنی هر شاخه حتماً یک فایل معمولی در .git/refs/heads دارد؛ Git میتواند اشارهگرها را در packed-refs هم ذخیره کند.
symbolic-ref prints the full branch name attached to HEAD; rev-parse prints the resolved commit ID. In the normal state, the latter two commands match for the current branch. Use show-ref to inspect refs rather than assuming each branch must be a loose file under .git/refs/heads; Git can store refs in packed-refs too.
ساختار داخلی Git را میشود یاد گرفت، اما ابزارهای plumbing پایدارتر از حدسزدن محل فیزیکی اشارهگر هستند. show-ref برای فهرستکردن اشارهگرهاست و symbolic-ref رابطهٔ HEAD را نشان میدهد. مسیر فیزیکی ذخیرهسازی جزو تضمین مدل ذهنی ما نیست.
Git’s internals are worth learning, but plumbing commands are safer than guessing where a ref is physically stored. show-ref lists refs and symbolic-ref reports HEAD’s relationship. The physical storage path is not the mental model we rely on.
اگر HEAD به شاخه اشاره نکند چه؟What if HEAD does not name a branch?
گاهی میخواهی یک commit قدیمی را فقط بررسی کنی. git switch --detach <commit> این کار را میکند: HEAD مستقیماً به commit میرسد. میتوانی فایلها را ببینی یا حتی commit بسازی؛ اما commit تازه اشارهگر شاخهای را جلو نمیبرد، چون شاخهای انتخاب نشده است.
Sometimes you only want to inspect an older commit. git switch --detach <commit> checks it out with HEAD pointing directly to that commit. You can inspect files or even commit, but no branch ref advances because no branch is selected.
git switch --detach main git symbolic-ref -q HEAD git status --short --branch 'temporary probe' | Out-File -Encoding utf8 probe.txt git add probe.txt git commit -m "Temporary detached experiment" git rev-parse HEAD git switch -c rescue/probe
در حالت جداشده، git symbolic-ref -q HEAD خروجی ندارد و با کد غیرصفر تمام میشود؛ اینجا یعنی HEAD symbolic نیست، نه اینکه مخزن خراب شده. فرمان آخر را بلافاصله بعد از commit اجرا کن تا شاخه روی همان commit ساخته شود. اگر قبلاً به شاخهٔ دیگری رفتهای، باید شناسهٔ commit را از شواهد مطمئن پیدا کنی و با git branch rescue/probe COMMIT_ID نامگذاریاش کنی؛ این فصل بازیابی آن شناسه را تضمین نمیکند.
In detached state, git symbolic-ref -q HEAD prints nothing and exits non-zero: HEAD is not symbolic, not broken. Run the final command immediately after the commit so the branch starts there. If you already switched away, identify the commit ID from reliable evidence and name it with git branch rescue/probe COMMIT_ID; this chapter does not guarantee recovery of that ID.
اگر از commit جداشده به شاخهای دیگر بروی، ممکن است هیچ شاخه اشارهگری به commit آزمایشی اشاره نکند. این به معنی حذف فوری شیء نیست، اما آن را بهعنوان راه بازیابی تضمینشده فرض نکن. اگر ارزش نگهداشتن دارد، قبل از switch کردن برایش شاخه بساز. فصل reflog بعداً سرنخهای بازیابی را جداگانه بررسی میکند.
After switching away from a detached commit, no branch ref may name the experiment. That does not mean the object disappears immediately, but do not treat that as a guaranteed recovery path. If it matters, create a branch before switching away. The reflog chapter will later cover recovery clues separately.
ساختن، رفتن، نامگذاری و کنارگذاشتن شاخهCreate, switch, rename, and retire branches
وقتی مدل اشارهگر روشن شد، فرمانها دیگر فهرستی برای حفظکردن نیستند. هرکدام پاسخی به یک وضعیتاند: ساختن نام، انتخاب شاخهٔ کاری، مرتبکردن نامها، یا حذف اشارهگری که دیگر لازم نیست.
Once the ref model is clear, commands are not a list to memorize. Each answers a situation: create a name, select working branch, tidy a name, or remove an unneeded ref.
| نیازNeed | فرمانCommand | شاهد / محدودیتEvidence / limit |
|---|---|---|
| ساختن شاخه بدون رفتن به آنCreate without switching | git branch feature/name | اشارهگر از commit فعلی آغاز میشود؛ شاخهٔ کاری عوض نمیشود.The ref starts at the current commit; the working branch stays selected. |
| ساختن و رفتن به شاخهCreate and switch | git switch -c feature/name | اگر switch موفق نشود، شاخه ساخته/تنظیم نمیشود.If switching fails, the branch is not created/reset. |
| دیدن شاخهها / جاریList / current branch | git branch --listgit branch --show-current | ستاره یعنی جاری؛ خروجی دوم نام را جدا میدهد.The asterisk marks current; the second command prints its name. |
| تغییر نام شاخهRename a branch | git branch -m old new | نام عوض میشود، commitها نه؛ برای شاخهٔ جاری هم کار میکند.The name changes, not commits; it also works for the current branch. |
| حذف امنتر اشارهگرSafer ref deletion | git branch -d name | اگر commitهای شاخه در upstream یا HEAD ادغامنشده باشند، معمولاً رد میشود.Usually refuses if the branch is not fully merged into its upstream or HEAD. |
-d و -D یکی نیستند: دومی بررسی ادغامشدن را نادیده میگیرد و اشارهگر را بهزور حذف میکند. این کار فایلهای پروژه را از worktree جاری پاک نمیکند؛ اما ممکن است commitهای منحصربهفرد دیگر شاخهٔ نامداری نداشته باشند. اگر دربارهٔ ارزششان مطمئن نیستی، حذف نکن. -D را فقط در مخزن دورریختنی این فصل آزمایش کن.
-d and -D differ: the latter skips the merged check and force-deletes the ref. It does not remove files from the current worktree, but unique commits may lose their branch name. If unsure whether they matter, keep the branch. Try -D only in this chapter’s disposable repository.
اگر main هنوز C1 باشد و آزمایش روی C2 فرزند آن، از دید گراف main میتواند با جلو رفتن مستقیم به C2 برسد؛ به این شکل fast-forward میگویند. فعلاً فقط رابطه را از git log --graph --oneline --decorate --all بخوان. فرمان ادغام و حالتهای دیگر را در فصل ۰۶ باز میکنیم.
If main is still at C1 and experiment is at child C2, main could move directly forward to C2—a fast-forward shape. For now, only read the relationship with git log --graph --oneline --decorate --all. Chapter 06 handles the merge operation and its other forms.
وقتی Git اجازهٔ حرکت نمیدهد، اول شاهد را بخوانWhen Git refuses a move, read the evidence first
اگر Git اجازهٔ جابهجایی یا حذف شاخه را نمیدهد، اول پیامش را بخوان. بیشتر وقتها دارد از کاری محافظت میکند که هنوز به جایی نرسیده، یا پوشهٔ کاری اجازهٔ جابهجایی امن نمیدهد. زور زدن با گزینهٔ اجباری باید آخرین تصمیم باشد، نه اولین واکنش.
A branch error usually does not mean Git is broken; often it is protecting a change or a name. Instead of forcing the operation, identify which condition is not met.
این نام شاخه از قبل هست
That branch name already exists
git branch experiment نام تکراری را overwrite نمیکند. با git branch --list و git show-ref --heads ببین اشارهگر موجود کجاست؛ نام تازه انتخاب کن یا همان شاخه را آگاهانه switch کن.
git branch experiment will not overwrite an existing name. Inspect with git branch --list and git show-ref --heads; choose a new name or deliberately switch to the existing branch.
تغییر محلی مانع switch شد
Local changes blocked the switch
پیام را بخوان و git status --short بزن. اگر فایل محلی در شاخهٔ مقصد فرق کند، switch ممکن است آن را overwrite کند و Git متوقف میشود. تغییر را commit کن یا با روشی آگاهانه نگهش دار؛ --discard-changes راهحل معمول نیست.
Read the message and run git status --short. If a local file differs in the target branch, switching could overwrite it, so Git stops. Commit or deliberately preserve the change; --discard-changes is not the routine fix.
حذف با -d رد شد
Deletion with -d was refused
Git میگوید اشارهگر هنوز commitهایی را نگه میدارد که در مرجع ادغامشدنش نیستند. این «آزار بیدلیل» نیست؛ قبل از حذف، commitها را با گراف مقایسه کن و تصمیم بگیر واقعاً باید نگهشان داری یا نه. -D بررسی را دور میزند، نه اینکه دادهٔ مهم را بیخطر کند.
Git is telling you the ref still protects commits not merged into its merge target. That is useful information. Compare the graph and decide whether those commits matter before deleting. -D bypasses the check; it does not make valuable data safe.
هشدار جداشده HEAD دیدی
You saw a detached HEAD warning
اگر فقط میخواهی ببینی، ادامه بده و commit نساز. اگر تغییرت ارزش دارد، قبل از رفتن به شاخهٔ دیگر git switch -c rescue/name بزن. نام شاخه است که مسیر آزمایش را بهوضوح نگه میدارد.
If you only want to inspect, continue without committing. If the work matters, run git switch -c rescue/name before switching elsewhere. The branch name explicitly retains the experiment.
تمرینها: از پیشبینی تا تصمیم امنExercises: from prediction to safe decisions
قبل از جواب هر تمرین، گراف را خیلی ساده بکش: اسم شاخه کجاست؟ HEAD به چه چیزی اشاره میکند؟ commit بعدی کدام اسم را جلو میبرد؟ همین سه علامت بیشتر سؤالهای شاخه را بدون حفظ دستور حل میکنند.
Predict first, then test in a disposable repository when useful. Open each solution to compare the reasoning with the result; commit IDs in your repository will differ.
روی commit C1 هستی و git branch demo میزنی. چند commit ساخته شد و اشارهگر تازه به کجا اشاره میکند؟
You are at C1 and run git branch demo. How many commits were created, and where does the new ref point?
پاسخ و دلیلAnswer and reasoning
هیچ commitی ساخته نمیشود. اشارهگر به همان C1 اشاره میکند؛ با git show-ref --heads دو نام و شناسهٔ یکسان را میبینی.
No commit is created. The ref points to C1; git show-ref --heads shows two names with the same ID.
خروجی میگوید abc123 refs/heads/main و abc123 refs/heads/test. چه چیزی را ثابت میکند و چه چیزی را نه؟
Output shows abc123 refs/heads/main and abc123 refs/heads/test. What does this prove, and what does it not?
بیا بازش کنیمInterpretation
هر دو اشارهگر در همین لحظه به یک commit حل میشوند. این ثابت نمیکند فایل جداگانهای ساخته شده یا دو نام همیشه با هم حرکت میکنند.
Both refs currently resolve to the same commit. It does not prove a separate file copy exists or that the names always move together.
روی main دو شاخه هممکاناند. به feature switch میکنی و commit میسازی. کدام شناسه عوض میشود؟
main and feature share a tip. You switch to feature and commit. Which ID changes?
راهحل و توضیحAnswer
شناسهٔ اشارهگر شاخهٔ قابلیت به commit تازه میرود؛ main همان commit قبلی را نگه میدارد. commit والد مشترک است.
The feature ref moves to the new commit; main keeps the old commit. Their previous tip is the shared parent.
میخواهی شاخهای به نام fix/timeout بسازی و همان لحظه روی آن کار کنی. کدام فرمان مستقیمتر است؟
You want to create fix/timeout and immediately work on it. Which command is direct?
چرا این جواب درست استReason
git switch -c fix/timeout هر دو کار را با هم انجام میدهد. اگر switch ناموفق باشد، عملیات ایجاد/تنظیم شاخه هم انجام نمیشود.
git switch -c fix/timeout does both. If switching fails, the branch is not created or reset.
git symbolic-ref HEAD خروجی refs/heads/experiment دارد. دقیقاً چه میدانی؟
git symbolic-ref HEAD prints refs/heads/experiment. What exactly do you know?
بررسی جوابPrecise answer
HEAD اکنون symbolic اشارهگر است و نام شاخهٔ آزمایش را دنبال میکند. برای دانستن commit آن، git rev-parse HEAD را جدا اجرا کن.
HEAD is currently a symbolic ref naming experiment. Run git rev-parse HEAD separately to learn its commit ID.
همتیمیات میگوید «git branch پروژه را دو بار کپی کرد». با یک شاهد CLI پاسخ بده.
A teammate says “git branch copied the project twice.” Give one CLI-based rebuttal.
پاسخ پیشنهادیUseful evidence
git show-ref --heads را قبل و بعد مقایسه کن: اشارهگر تازه با همان شناسه شیء commit ظاهر میشود و هیچ commit تازهای لازم نیست. این آزمایش اشارهگر را نشان میدهد، نه مصرف دیسک کل پایگاه اشیای Git را.
Compare git show-ref --heads before and after: a new ref appears with the same commit OID, with no new commit required. This demonstrates refs, not total object-store disk usage.
از اینجا به بعد شاخه را فقط اسم نبین؛ حرکتش را روی گراف دنبال کن. هر commit جدید باید یک سؤال ساده داشته باشد: الان HEAD روی کدام شاخه است و کدام اشارهگر قرار است جلو برود؟
From here, do not see a branch as just a name; follow its movement on the graph. For every new commit ask one simple question: which branch does HEAD name, and which pointer will move?
فرمان git branch demo میگوید شاخه از قبل وجود دارد. قدم بعدی چیست؟
git branch demo says the branch already exists. What next?
پاسخ و دلیلNext step
اول git branch --list و git show-ref --heads را ببین تا محل اشارهگر روشن شود. اگر همان شاخه مدنظر است، آگاهانه switch کن؛ ایجاد دوباره لازم نیست.
Inspect git branch --list and git show-ref --heads first. If it is the intended branch, switch to it; recreating it is unnecessary.
switch به main بهخاطر تغییر محلی overwriteپذیر متوقف شد. آیا باید --discard-changes بزنی؟ چرا؟
Switching to main stopped because a local change could be overwritten. Should you use --discard-changes? Why?
بیا بازش کنیمSafe decision
نه، نه پیش از فهمیدن تغییر. git status --short و پیام خطا مشخص میکنند کدام فایل در خطر است. تغییر را commit یا آگاهانه حفظ کن؛ discard ممکن است همان کاری را دور بریزد که قصد نگهداشتنش را داشتی.
Not before understanding the change. git status --short and the error identify the at-risk file. Commit or deliberately preserve it; discard may throw away exactly what you meant to keep.
چرا git show-ref --heads از بازکردن دستی .git/refs/heads/main بهتر است؟
Why prefer git show-ref --heads over manually opening .git/refs/heads/main?
راهحل و توضیحExplanation
ممکن است اشارهگرها بستهبندیشده باشند و فایل جدا جدا نداشته باشند. فرمان Git فهرست اشارهگرهای محلی را از مسیرهای ذخیرهسازی پشتیبانیشده حل میکند.
Refs may be packed rather than present as loose files. Git’s command lists local refs regardless of the supported storage form.
در خروجی گراف، main روی C1 و feature روی فرزند C2 است. بدون اجرای merge، چه میتوانی بگویی؟
In the graph, main names C1 and feature names child C2. What can you say without running a merge?
چرا این جواب درست استScope of the conclusion
گراف شکل fast-forward دارد: main میتواند با جابهجایی اشارهگر به C2 برسد، چون C2 فرزند آن است. این پیشبینی شکل گراف است، نه انجام ادغام؛ عملیات در فصل بعد است.
The graph has fast-forward shape: main could move to C2 because it is a descendant. This predicts graph geometry; it does not perform a merge, which comes next.
روی main فایل ردیابیشده را تغییر دادهای ولی commit نکردهای؛ همان فایل در آزمایش محتوای دیگری دارد. چرا switch ممکن است رد شود؟
You changed a tracked file on main without committing; experiment has different content at that path. Why might switching be refused?
بررسی جوابAnswer
بهروزرسانی worktree ممکن است تغییر محلی را overwrite کند. Git معمولاً برای حفظ آن متوقف میشود. پیام و وضعیت را بخوان؛ شاخه اشارهگر عوض نشده چون switch کامل نشده است.
Updating the worktree could overwrite the local change, so Git stops to preserve it. Read the message and status; the branch ref was not switched because the operation did not complete.
فقط میخواهی محتوای commit قدیمی را بخوانی و تغییرت لازم نیست بماند. کدام روش مناسب است؟
You only want to inspect an old commit and do not need to keep changes. Which approach fits?
پاسخ پیشنهادیReason
git switch --detach COMMIT برای بررسی commit بدون ساخت شاخه است. اگر آزمایش را commit کردی و خواستی حفظش کنی، قبل از خروج شاخه بساز.
git switch --detach COMMIT inspects a commit without creating a branch. If you commit an experiment and want to keep it, create a branch before leaving.
تمرینهای آخر detached HEAD و حذف شاخه را وارد میکنند. اینجا عجله با گزینههای اجباری خطرناک است. اول ببین commit هنوز از چه نامی قابل دسترسی است، بعد تصمیم بگیر.
The final exercises introduce detached HEAD and branch deletion. Force options are risky here. First determine which name still reaches the commit, then decide what to do.
در جداشده HEAD، git symbolic-ref -q HEAD ساکت و non-zero تمام میشود. آیا این خطای مخزن است؟
In detached HEAD, git symbolic-ref -q HEAD is silent and exits non-zero. Is the repository broken?
پاسخ و دلیلInterpret the result
نه. این یعنی HEAD symbolic اشارهگر نیست و مستقیماً commit را نام میبرد. برای تشخیص حالت کلی git status --short --branch را هم ببین.
No. HEAD is not a symbolic ref and names a commit directly. Also inspect git status --short --branch for the broader state.
در جداشده حالت commit ارزشمندی ساختی و هنوز جایی نرفتهای. کوتاهترین راه حفظش چیست؟
You made a valuable detached commit and have not switched away. What is the shortest way to preserve it?
بیا بازش کنیمSolution
git switch -c rescue/my-work یک شاخه از commit فعلی میسازد و همزمان روی آن میرود. بعد git show-ref --heads را بررسی کن تا نام تازه را ببینی.
git switch -c rescue/my-work creates a branch at the current commit and switches to it. Verify the new name with git show-ref --heads.
حذف feature با git branch -d feature رد شد. آیا -D فقط فایلهای شاخه را پاک میکند؟
Deleting feature with git branch -d feature was refused. Does -D merely remove that branch’s files?
راهحل و توضیحWhy pause?
-D اشارهگر را بدون بررسی ادغام حذف میکند. این حذف، پاککردن فایلهای worktree نیست؛ خطرش این است که commitهای یکتا دیگر نام شاخه نداشته باشند. ابتدا گراف و ارزش commitها را بررسی کن.
-D removes the ref without the merged check. It does not delete worktree files; the risk is that unique commits lose their branch name. Inspect the graph and decide whether those commits matter first.
دو شاخه یک commit مشترک دارند. dev روی A commit میسازد؛ همزمان اشارهگر شاخهٔ B هم جلو میرود. آیا این رفتار طبیعی است؟
Two branches share a commit. dev commits on A, and B’s ref advances too. Is that expected?
چرا این جواب درست استWhat to check
در مدل معمول، خیر. ببین واقعاً روی کدام شاخه بودهای: git branch --show-current و git symbolic-ref HEAD؛ سپس شناسه شیءها را با show-ref مقایسه کن. شاید B عمداً reset/force-update شده یا اسکریپتی اشارهگر را تغییر داده باشد.
Not in the normal model. Check the selected branch with git branch --show-current and git symbolic-ref HEAD, then compare OIDs with show-ref. B may have been deliberately reset/force-updated, or a script may have changed it.
توسعهدهنده میگوید commit کرده، اما نام شاخهاش جلو نرفته. git status حالت جداشده نشان میدهد. علت محتمل و قدم امن بعدی؟
A developer committed, but their branch name did not move. git status says detached. Likely cause and safe next step?
بررسی جوابAnalysis
commit در حالی ساخته شده که HEAD به شاخه اشاره نمیکرد، پس شاخه اشارهگری جلو نرفته. اگر هنوز روی commit هست، git switch -c rescue/committed-work بزن؛ اگر جابهجا شده، commit شناسه را از شواهد موجود مشخص کن و اشارهگر بساز. این فصل بازیابی از reflog را تضمین نمیکند.
The commit was made while HEAD named no branch, so no branch ref advanced. If still there, run git switch -c rescue/committed-work; if you moved away, identify the commit ID from available evidence and create a ref. This chapter does not promise reflog recovery.
تیمی شاخهٔ کوتاهعمر قابلیت را بعد از بررسی میخواهد کنار بگذارد، اما مطمئن نیست ادغام شده یا نه. چه شواهدی قبل از حذف میخواهی؟
A team wants to retire a short-lived feature branch but is unsure whether it was integrated. What evidence do you want before deletion?
پاسخ پیشنهادیPre-deletion check
گراف همهٔ شاخهها را با git log --graph --oneline --decorate --all ببین و وضعیت ادغامشدن را با git branch --merged main مقایسه کن. سپس تصمیم بگیر. اگر مطمئن نیستی، اشارهگر را نگه دار؛ -D فقط پیام هشدار را دور میزند.
Inspect all tips with git log --graph --oneline --decorate --all and compare merged status using git branch --merged main. Decide afterward. If uncertain, keep the ref; -D only bypasses the warning.
پروژهٔ کوچک: آزمایش موازی بدون کپی پوشهMini-project: parallel experiments without copying a folder
اینجا میخواهیم چیزی را که خیلیها با کپی پوشه انجام میدهند، با شاخه انجام دهیم: دو آزمایش موازی، یک تاریخچهٔ مشترک، بدون دو نسخهٔ مبهم از پروژه. در پایان باید بتوانی از روی گراف نشان بدهی هر آزمایش کجا جدا شد و کدام را نگه داشتی.
A team needs to compare two implementations of a function. It should not duplicate the folder or jump into merging before learning it. Create two paths from one starting point, document the graph, retire one experiment only in a disposable repo, and retain the other.
- نقطهٔ مشترک بسازCreate a shared starting point
در پوشهٔ جدا،
git init -b mainاجرا کن و یک فایلchoice.txtبا مقدارbaselinecommit کن. شناسه شیء را یادداشت کن.In a separate folder, run
git init -b mainand commitchoice.txtwith valuebaseline. Record its OID. - راه A را آزمایش کنTry approach A
با
git switch -c experiment/aشاخه بساز، فایل را بهapproach Aتغییر بده و commit کن. سپسgit show-ref --headsرا ثبت کن.Create
experiment/awithgit switch -c experiment/a, change the file toapproach A, and commit. Recordgit show-ref --heads. - از main، راه B را بسازStart approach B from main
به
mainبرگرد وexperiment/bرا از همان نقطه بساز. مقدارapproach Bرا commit کن. این دو راه باید والد مشترک و نوک متفاوت داشته باشند.Return to
mainand createexperiment/bfrom that same point. Commitapproach B. The two paths should share a parent and have distinct tips. - گراف را بخوان و تصمیم را ثبت کنRead the graph and record a decision
با
git log --graph --oneline --decorate --allوgit branch --show-currentثابت کن هر دو آزمایش از کجا آغاز شدند و اکنون کدام فعال است. توضیح بده این گراف هنوز merge نشده است.Use
git log --graph --oneline --decorate --allandgit branch --show-currentto prove the common start and current branch. State that the graph has not been merged. - A را آگاهانه کنار بگذار، B را نگه دارRetire A deliberately; keep B
ابتدا شناسه شیء آخرین commit A را ثبت و با
git show OIDبررسی کن. بعد، فقط چون این مخزن دورریختنی است،git branch -D experiment/aرا اجرا کن. باshow-refثابت کن B باقی است و در گزارش بنویس حذف اشارهگر، commit را بهعنوان شاخه نگه نمیدارد؛ روی دادهٔ کاری واقعی این کار را تکرار نکن.Record A’s tip OID and inspect it with
git show OID. Only because this repository is disposable, rungit branch -D experiment/a. Prove B remains withshow-ref, and note that deleting the ref no longer retains that commit as a branch; do not repeat this casually on real work.
راهنمایی: اگر دو آزمایش از هم جدا نشدندHint: if the experiments did not diverge
به جای ساخت B از شاخهٔ A، اول به main برگرد. شناسه شیء والد هر دو را با git rev-parse و نمودار گراف کنترل کن.
Return to main before creating B rather than branching B from A. Compare parent OIDs with git rev-parse and inspect the graph.
یک تصویر/خروجی از گراف قبل از حذف، شناسه شیءهای main و A و B، دلیل انتخاب راه B، و خروجی اشارهگرها بعد از کنارگذاشتن A. پروژه وقتی کامل است که بتوانی تفاوت «شاخهٔ دیگر را انتخاب کردم» و «commit جدید ساختم» را با همین شواهد توضیح بدهی.
Submit the graph before deletion, OIDs for main/A/B, why you chose B, and refs after retiring A. The project is complete when you can use that evidence to distinguish switching branches from creating a commit.
دو مسیر ساخته شد؛ حالا چطور دوباره به هم میرسند؟Two paths exist—how do they come together?
حالا دو اسم میتوانند به دو مسیر متفاوت در همان گراف برسند. سؤال طبیعی بعدی این است: وقتی هر دو مسیر تغییر مفید دارند، چطور دوباره به هم میرسند؟ فصل بعد دقیقاً از همین نقطه شروع میشود.
You now know why a branch is not a project copy, why commits advance only the current branch ref, and why HEAD can be detached. The team’s next question remains: how do we bring a good change back, and what if two people changed the same area?
فصل بعد سراغ merge میرویم: fast-forward را واقعاً اجرا میکنیم، merge commit را از گراف میخوانیم و تعارض را بهعنوان نتیجهٔ طبیعی دو مسیر بررسی میکنیم—نه نشانهٔ خرابشدن Git.
Next, we will actually merge: perform a fast-forward, read a merge commit in the graph, and treat conflicts as a natural result of two lines of work—not evidence that Git is broken.
نقشهٔ سریع شاخههاQuick branch reference
| اگر میخواهی…If you want to… | شروع کن باStart with | قبل از نتیجهگیری یادت باشدBefore concluding, remember |
|---|---|---|
| اشارهگرها را ببینیList refs | git show-ref --heads | شناسه شیء یک شناسهٔ شیء است؛ نام اشارهگر جای خود را دارد.An OID identifies an object; the ref name is separate. |
| شاخهٔ جاری را بفهمیIdentify current branch | git branch --show-currentgit symbolic-ref HEAD | خروجی symbolic-ref در جداشده خالی/non-zero است.symbolic-ref is empty/non-zero when detached. |
| گراف شاخهها را ببینیSee branch graph | git log --graph --oneline --decorate --all | این نمایش است؛ گراف را تغییر نمیدهد.This is a view; it does not change the graph. |
| یک commit را حفظ کنیRetain a commit | git switch -c rescue/name | در جداشده، نام شاخه را قبل از ترک commit بساز.In detached state, create the branch before leaving the commit. |