merge و حل تعارض
Merging and resolving conflicts
دو نفر از یک نقطه شروع میکنند و هرکدام چیزی را تغییر میدهند. بعضی وقتها Git بدون حرف اضافه آنها را کنار هم میگذارد؛ بعضی وقتها میایستد و از تو تصمیم میخواهد. این فصل میفهمیم چرا.
Two teammates started at one commit. One improved the greeting; the other changed the team list. Git combined both quietly. Next time, they rewrote the same greeting line differently, and Git stopped. Why could it combine the first pair but not the second?
یک بار Git جلو رفت؛ بار دوم مکث کردGit moved forward once, then paused
در فصل ۰۵ دیدیم دو شاخه میتوانند از یک commit شروع شوند و هرکدام اشارهگر مستقلی داشته باشند. حالا وقت برگرداندن یک مسیر به شاخهٔ اصلی است. اول پیشبینی کن: اگر شاخهٔ قابلیت فقط یک commit جلوتر باشد و main هیچ commit تازهای نداشته باشد، آیا Git باید commit جدیدی بسازد؟
Chapter 05 showed two branches starting at one commit, each with its own ref. Now we need to bring one line back to the main branch. Predict first: if feature is one commit ahead and main has not added a commit, must Git create a new commit?
برای آزمایش، مخزن جداگانه میسازیم. یک شاخه جلو میرود؛ بعد main آن را merge میکند. فایل اصلی را نگاه کن، اما مهمتر از آن، شناسهها و شکل گراف را بخوان.
We’ll use a separate repository. One branch advances, then main merges it. Inspect the file, but more importantly, compare commit IDs and graph shape.
mkdir merge-lab && cd merge-lab git init -b main git config user.name 'Nika Example' git config user.email 'nika@example.test' printf 'سلام\n' > greeting.txt git add greeting.txt git commit -m 'Add greeting' git switch -c feature/welcome printf 'سلام به همه\n' > greeting.txt git add greeting.txt git commit -m 'Improve greeting' git switch main git rev-parse HEAD git rev-parse feature/welcome git merge feature/welcome git log --oneline --graph --decorate --all
قبل از merge، main نیاکان مستقیم feature/welcome است؛ شاخهها از هم جدا نشدهاند. merge پیشفرض فقط اشارهگر شاخهٔ جاری را به commit جلوتر میبرد. اگر دو شناسه آخر را بعد از merge مقایسه کنی، main و قابلیت روی یک commitاند و commit سومی ساخته نشده است. این حرکت مستقیم را fast-forward میگوییم.
Before merging, main is a direct ancestor of feature/welcome; the tips have not diverged. The default merge simply advances the current branch ref to the descendant. Afterward, the two refs resolve to the same commit, and no third commit was created. That direct move is a fast-forward.
| شکل تاریخچهHistory shape | کار معمول mergeDefault merge result | commit تازه؟New commit? |
|---|---|---|
| نوک شاخهٔ جاری نیاکان شاخهٔ ورودی است.Current tip is an ancestor of the incoming tip. | اشارهگر شاخهٔ جاری به نوک جلوتر حرکت میکند.Current branch ref advances to the descendant. | نه؛ fast-forward است.No; it is a fast-forward. |
| هر دو شاخه از نقطهٔ مشترک جلو رفتهاند.Both branches advanced from a shared point. | Git نتیجه را ادغام و commit دووالدی میسازد.Git combines the result and creates a two-parent commit. | معمولاً بله؛ merge commit.Usually yes; a merge commit. |
| نوک ورودی از قبل در تاریخچهٔ جاری است.Incoming tip is already in current history. | پیام Already up to date.“Already up to date.” | نه؛ چیزی برای افزودن نیست.No; there is nothing new to add. |
وقتی هر دو شاخه جلو رفتهاندWhen both branches have advanced
حالا شکل سختتر را بسازیم. بعد از C1، main روی C2 تغییر مستقلی میدهد و قابلیت هم روی C3 تغییر خودش را. هیچکدام نیاکان دیگری نیست. Git برای ساخت نتیجه باید بفهمد هر طرف از نقطهٔ مشترک چه تغییری داده است.
Now create the harder shape. After C1, main makes an independent change at C2 and feature makes its own at C3. Neither tip is an ancestor of the other. To build a result, Git must determine what each side changed since their shared point.
git switch -c feature/notes printf 'notes enabled\n' > notes.txt git add notes.txt git commit -m 'Add notes' git switch main printf 'release=1\n' > release.txt git add release.txt git commit -m 'Record release' git merge-base main feature/notes git merge feature/notes git rev-list --parents -n 1 HEAD git log --oneline --graph --decorate --all
در این مثال تغییرها به فایلهای جدا میرسند، پس میتوانند بیتعارض کنار هم قرار بگیرند. چون نوکها واگرا شدهاند، Git commit تازهای میسازد که دو والد دارد: نوک قبلی main و نوک feature. git rev-list --parents -n 1 HEAD یک شناسه برای merge commit و دو شناسه والد نشان میدهد.
Here the changes touch different files, so Git can combine them without conflict. Because the tips diverged, Git creates a new commit with two parents: the former main tip and the feature tip. git rev-list --parents -n 1 HEAD prints the merge commit ID followed by its two parent IDs.
این نقطهٔ مشترک را merge base مینامیم. در گرافهای ساده یک merge base روشن داریم؛ تاریخچههای پیچیدهتر ممکن است چند بهترین merge base داشته باشند و الگوریتم پیشفرض آنها را هم مدیریت میکند. فعلاً لازم نیست در آن پیچیدگی فرو برویم. نکتهٔ امروز این است: Git فقط دو فایل آخر را کنار هم نمیگذارد؛ تغییرهای هر دو را از پایهٔ مشترک میسنجد.
That shared point is the merge base. Simple graphs have one obvious base; more complex histories can have multiple best merge bases, which the default strategy can handle. We do not need that complexity yet. The key idea is that Git does not merely compare two final files—it evaluates both sides' changes from their common base.
--ff-only فقط وقتی جلو میرود که نوک فعلی نیاکان شاخهٔ ورودی باشد؛ اگر مسیرها واگرا باشند، متوقف میشود. --no-ff حتی در حالت امکان fast-forward، merge commit میسازد تا مرز ادغام در تاریخچه بماند. هیچکدام همیشه بهترین نیست؛ تیم باید بر اساس نیاز به گراف خطی یا ثبت مرز کار تصمیم بگیرد.
--ff-only proceeds only when the current tip is an ancestor of the incoming tip; it refuses diverged histories. --no-ff creates a merge commit even when a fast-forward is possible, preserving an explicit merge boundary. Neither is universally best; choose based on whether your team wants a linear graph or an explicit integration record.
تعارض یعنی نیتِ لازم را Git نمیداندA conflict means Git lacks the intent it needs
تا وقتی دو تغییر در بخشهای جدا باشند، Git معمولاً میتواند هر دو را نگه دارد. اما اگر main یک خط را «سلام به محصول» و قابلیت همان خط را «سلام به مهندسی» کند، کدامیک باید نسخهٔ نهایی باشد؟ این سؤال فنی نیست؛ تصمیم محصول است.
When changes affect separate regions, Git can usually retain both. But if main changes one line to “Hello product” and feature changes that same line to “Hello engineering,” which should the final version say? That is a product decision, not a technical fact.
Git خراب نشده. ابزار merge تغییرها را نسبت به پایه تشخیص میدهد، اما قرارداد تیم یا معنای متن را نمیداند. وقتی دو تغییر با هم ناسازگارند، Git بهجای انتخاب تصادفی merge را متوقف میکند تا تو تصمیم بگیری.
Git is not broken. The merge machinery can compare changes against the base, but it does not know the team's contract or the meaning of the text. When edits are incompatible, Git stops rather than choosing arbitrarily.
| تغییر دو طرفBoth sides changed… | نتیجهٔ محتملLikely result | چه چیزی هنوز لازم است؟What is still needed? |
|---|---|---|
| فایلهای متفاوتDifferent files | ادغام خودکارAutomatic combination | بازبینی حاصل؛ ادغام خودکار تضمین درستی معنایی نیست.Review the result; automatic does not mean semantically correct. |
| یک فایل، ناحیههای جداOne file, separate regions | اغلب ادغام خودکارOften automatic | کنترل کن که دو تغییر کنار هم معنی بدهند.Check that both edits make sense together. |
| یک ناحیه، محتوای ناسازگارSame region, incompatible content | تعارض و توقف mergeConflict; merge pauses | انسان باید قصد نهایی را انتخاب/ترکیب کند.A human must choose or combine the intended result. |
| یک طرف حذف، دیگری تغییرOne deletes, the other modifies | تعارض modify/deleteModify/delete conflict | تصمیم بگیر فایل بماند یا حذف شود.Decide whether the file should remain or be deleted. |
حل تعارض: اول قصد، بعد ویرایشResolve a conflict: intent before editing
تعارض را در یک مخزن دورریختنی میسازیم. قبل از merge، git status باید بدون تغییر باشد؛ این کار احتمال گیرکردن تغییرهای نامرتبط را کم میکند و abort را قابلپیشبینیتر نگه میدارد. اگر پروژهٔ واقعی داری، اول تغییرها را commit کن؛ merge را روی پوشهٔ نیمهکاره امتحان نکن.
We’ll create a conflict in a disposable repository. Before merging, git status should be clean; this reduces the chance that unrelated work becomes entangled and makes abort more predictable. In a real project, commit your work first—do not experiment on a half-finished tree.
printf 'سلام به تیم\n' > greeting.txt git add greeting.txt git commit -m 'Add shared greeting' git switch -c feature/product printf 'سلام به تیم محصول\n' > greeting.txt git add greeting.txt git commit -m 'Address product team' git switch main printf 'سلام به تیم مهندسی\n' > greeting.txt git add greeting.txt git commit -m 'Address engineering team' git status --short git merge feature/product
پیام CONFLICT (content) و Automatic merge failed یعنی Git ترکیب نهایی را ثبت نکرده و merge هنوز در جریان است. این خطا نمیگوید کدام جمله بهتر است. فایل را باز کن و قبل از دستزدن به نشانگرها تصمیم بگیر: آیا باید محصول، مهندسی، یا هر دو در متن باشند؟
A CONFLICT (content) message and Automatic merge failed mean Git did not record a final combination and the merge is still in progress. The error does not say which sentence is better. Open the file and decide before touching markers: should the message address product, engineering, or both?
<<<<<<< HEAD
سلام به تیم مهندسی
=======
سلام به تیم محصول
>>>>>>> feature/productدر این merge مشخص، بخش بالای ======= نسخهٔ ours یعنی main فعلی است؛ بخش پایین نسخهٔ theirs یعنی feature/product. نشانگرها دستور Git نیستند؛ متن راهنما هستند که باید از نسخهٔ نهایی حذف شوند. انتخاب یک طرف هم همیشه جواب نیست: شاید جملهٔ درست «سلام به تیم محصول و مهندسی» باشد.
In this merge, the section above ======= is ours—the current main branch; below it is theirs—feature/product. Markers are not Git commands; they are guidance that must not remain in the final version. Choosing one side is not always right: perhaps the intended line is “Hello product and engineering teams.”
git add نتیجه را وارد ناحیه آمادهسازی میکند.git add.فایل را به جملهٔ موردنظر تبدیل کن؛ سپس خودت دوباره بخوانش، نشانگرها را جستوجو کن و بعد ناحیه آمادهسازی کن:
Edit the file to the intended sentence; then read it back, search for leftover markers, and only then stage it:
printf 'سلام به تیم محصول و مهندسی\n' > greeting.txt git diff --check git diff -- greeting.txt git add greeting.txt git status git diff --cached git merge --continue git status --short --branch git log --oneline --graph --decorate --all
پس از git add، مسیر از ناحیه آمادهسازیهای unmerged به یک رد عادی ناحیه آمادهسازی 0 میرود. git merge --continue merge commit را کامل میکند؛ git commit هم میتواند آن را تمام کند. قبل از ادامه، diff آمادهشده را بازبینی کن. موفقیت یعنی هم فایل نهایی قصد را نشان بدهد، هم وضعیت از حالت merge بیرون آمده باشد.
After git add, the path moves from unmerged index entries to one ordinary stage-0 entry. git merge --continue completes the merge commit; git commit can complete it too. Review the staged diff first. Success means the file expresses the intended result and status no longer reports a merge in progress.
سه نسخهٔ حلنشده در ناحیه آمادهسازی کجا هستند؟Where do the three unresolved versions live?
وقتی نشانگر را در فایل میبینی، شاید فکر کنی Git فقط یک متن مبهم روی دیسک گذاشته. در واقع ناحیه آمادهسازی هم تا زمان حل، نسخههای جداگانه را نگه میدارد. با git ls-files -u میتوانی ببینی هر مسیر چه ردهای unmerged دارد.
When you see markers in a file, it may look as if Git left only one ambiguous text on disk. In fact, the index retains separate versions until resolution. git ls-files -u shows the unmerged entries for each path.
| ناحیه آمادهسازیStage | نسخهVersion | در این مثالIn this example |
|---|---|---|
1 | merge basemerge base | خط اصلی قبل از جداشدن دو branch.The original line before the branches diverged. |
2 | oursours | نسخهٔ شاخهٔ جاری، یعنی HEAD این merge.The current branch version—HEAD for this merge. |
3 | theirstheirs | نسخهٔ شاخهٔ ورودی، یعنی MERGE_HEAD.The incoming branch version—MERGE_HEAD. |
git ls-files -u
100644 15ab... 1 greeting.txt
100644 91cd... 2 greeting.txt
100644 6ef0... 3 greeting.txt
git show :1:greeting.txt
git show :2:greeting.txt
git show :3:greeting.txtسه شناسهٔ متفاوت نشان میدهد Git نسخهٔ پایه، ours و theirs را حفظ کرده است؛ هر سه مربوط به یک مسیرند. بعد از حل و git add، git ls-files -u برای آن مسیر دیگر چیزی چاپ نمیکند. این فقط میگوید ناحیه آمادهسازی آن مسیر unmerged نیست؛ درستبودن معنایی متن را باید خودت بازبینی کنی.
Three different IDs show that Git retained the base, ours, and theirs for one path. After resolving and running git add, git ls-files -u prints nothing for that path. This proves the index is no longer unmerged; you must still review whether the text is semantically correct.
در توضیح این فصل ours یعنی شاخهٔ جاریِ merge و theirs یعنی شاخهی که به git merge دادهای. این نامها «کار من/کار همکارم» نیستند و در عملیات دیگری مثل rebase ممکن است از زاویهٔ متفاوتی معنا شوند. فعلاً shortcutهایی مثل --ours و --theirs را حفظ نکن؛ اول ناحیه آمادهسازیها و گراف را بفهم.
In this chapter, ours means the current branch being merged into, and theirs means the branch passed to git merge. These labels do not mean “my work/the teammate’s work,” and other operations such as rebase can use a different perspective. Do not memorize --ours/--theirs shortcuts yet; understand the graph and stages first.
تعارض همیشه دو متن کنار هم نیستA conflict is not always two text blocks
فرض کن main یک سیاست را اصلاح میکند و شاخهٔ دیگر همان فایل را حذف میکند. اینجا نشانگرهای سهخطی داخل فایل کافی نیستند؛ سؤال انسانی این است که آیا فایل هنوز لازم است؟ Git نمیتواند این تصمیم را از تاریخچه حدس بزند.
Suppose main updates a policy while another branch deletes the same file. Three text-marker lines cannot express this decision. The human question is whether the file is still needed—a decision Git cannot infer from history.
git status git ls-files -u git show :1:policy.txt git show :2:policy.txt git show :3:policy.txt
در این نمونه فرض کردهایم ours فایل را تغییر داده و theirs حذفش کرده؛ ناحیه آمادهسازی 1 و 2 وجود دارند، اما ناحیه آمادهسازی 3 ممکن است اصلاً نباشد. نبودن ناحیه آمادهسازی یعنی آن سمت برای این مسیر فایل نداشته؛ فرمان git show :3:policy.txt پس شکست میخورد. در حالت برعکس، ناحیه آمادهسازی 2 غایب میشود. همیشه خروجی ls-files -u را بخوان، نه اینکه وجود هر سه را فرض بگیری.
Here we assume ours modified the file and theirs deleted it: stages 1 and 2 exist, but stage 3 may be absent. A missing stage means that side had no file at this path, so git show :3:policy.txt fails. In the reverse case, stage 2 is absent. Read ls-files -u; never assume all three exist.
اگر تصمیم این است که نسخهٔ اصلاحشده بماند، فایل را بازبینی و با git add policy.txt ثبت کن. اگر باید حذف بماند، git rm policy.txt را بزن. هر دو فرمان به Git میگویند «این تصمیم نهایی من برای ناحیه آمادهسازی است»؛ هیچکدام جای تصمیم را نمیگیرند.
If the decision is to keep the revised file, review it and stage it with git add policy.txt. If deletion is intended, use git rm policy.txt. Either tells Git “this is my final index decision”; neither makes the decision for you.
اگر تصمیم نداریم، یا کار اشتباه پیش رفتWhen you cannot decide—or the merge went wrong
وسط تعارض لازم نیست قهرمانبازی دربیاوری. اگر هنوز تصمیم درست را نمیدانی، وضعیت را ثبت کن و در صورت نیاز merge را لغو کن. مهم این است که بفهمی Git کجا متوقف شده و کدام تصمیم را از انسان میخواهد.
You do not have to rush through a conflict. If the tree was clean before the merge and you do not have a decision yet, you can abort the in-progress merge. But if you had unfinished work, do not treat abort as a guaranteed rewind button.
git status git merge --abort git status --short --branch git log --oneline --graph --decorate --all
git merge --abort تلاش میکند وضعیت قبل از merge را بازسازی کند. اگر تغییرهای محلیِ ثبتنشده هنگام شروع داشتهای—بهخصوص اگر بعد از آغاز merge آنها را دستکاری کرده باشی—ممکن است بازسازی کامل ممکن نباشد. برای همین پیشبررسی مهم است: git status، و اگر در کار واقعی چیزی ناتمام است، اول آن را جداگانه ثبت یا حفظ کن.
git merge --abort tries to reconstruct the pre-merge state. If you had uncommitted changes when it began—especially if you edited them further after the merge started—Git may not be able to reconstruct everything. That is why the pre-check matters: inspect git status, and preserve unfinished real work separately before merging.
Already up to date دیدی
You saw “Already up to date”
این شکست نیست. نوک شاخهٔ ورودی از قبل در تاریخچهٔ شاخهٔ جاری هست. با git log --graph --decorate --oneline --all مطمئن شو شاخه درست را merge کردهای؛ اگر انتظار commit تازه داشتی، شاید روی شاخه اشتباهی ایستادهای.
This is not a failure. The incoming tip is already in current history. Verify the graph with git log --graph --decorate --oneline --all; if you expected new commits, perhaps you are on the wrong branch.
قبل از حل، commit زدی
You tried to commit before resolving
Git ناحیه آمادهسازی مسیرهای unmerged دارد و اجازهٔ commit معمولی نمیدهد. git status را بخوان، هر مسیر را با قصد درست ویرایش کن و ناحیه آمادهسازی کن. بعد git merge --continue یا git commit.
The index still has unmerged paths, so a normal commit cannot finish. Read git status, edit each path to the intended result, and stage it. Then use git merge --continue or git commit.
برای خلاصشدن نشانگرها را پاک کردی
You deleted markers just to make them disappear
حذف نشانگر کافی نیست؛ ممکن است یکی از جملههای لازم را هم حذف کرده باشی. با git show :1:FILE، :2: و :3: نسخهها را ببین، قصد نهایی را بنویس، سپس diff را بخوان و ناحیه آمادهسازی کن.
Removing markers is not enough; you may have deleted needed content too. Inspect versions with git show :1:FILE, :2:, and :3:, write the intended result, review the diff, and stage it.
تغییر نامرتبط را قبل از merge نگه داشتی
Unrelated work was left dirty before merging
merge ممکن است برای محافظت از آن متوقف شود یا تغییرهای همپوشان را در معرض خطر بگذارد. git status --short و پیام خطا را بخوان. در مخزن آزمایشی پاک شروع کن؛ در کار واقعی قبل از merge تغییرها را commit یا با روشی مطمئن نگه دار. force نکن.
The merge may stop to protect that work or put overlapping changes at risk. Read git status --short and the message. Start clean in experiments; in real work, commit or safely preserve changes before merging. Do not force it.
یک طرف را انتخاب کردی و بخش لازم از طرف دیگر پرید
Choosing one side dropped needed content
از روی نام ours/theirs تصمیم نگیر. مرجع و قصد را بخوان، اگر لازم است سه نسخه را از ناحیه آمادهسازی بیرون بکش، متن نهایی را بنویس و با git diff --cached ثابت کن که هر دو نیاز مهم باقی ماندهاند.
Do not decide from the labels alone. Read the context and intent, inspect the three versions if needed, write the final text, and use git diff --cached to confirm both required ideas remain.
تمرینها: از پیشبینی گراف تا تصمیم انسانیExercises: from graph prediction to human decisions
در تمرینها اول گراف را نگاه کن، بعد متن فایل را. بعضی سؤالها اصلاً تعارض ندارند و فقط با حرکت شاخه حل میشوند؛ بعضیها merge commit میسازند و بعضیها واقعاً به تصمیم انسانی نیاز دارند.
Predict before opening the solution. The exercises move from reading a graph to incidents where Git evidence and product decisions must be separated.
main روی C1 است و قابلیت به C2 فرزند C1 رسیده؛ main commit دیگری ندارد. merge پیشفرض چه میکند؟
main is at C1; feature is at child C2; main has no other commit. What does a default merge do?
راهنماییHint
آیا دو نوک شاخه واگرا شدهاند؟
Have the branch tips diverged?
پاسخ و دلیلAnswer with evidence
fast-forward: اشارهگر main به C2 میرود، commit تازه ساخته نمیشود. بعد از merge، git rev-parse main و git rev-parse feature باید یک شناسه بدهند.
A fast-forward: main moves to C2 without a new commit. Afterward, git rev-parse main and git rev-parse feature should return the same ID.
main و topic هر دو یک commit مستقل از B ساختهاند. آیا main میتواند فقط اشارهگر را به نوک topic حرکت دهد و تاریخچهٔ هر دو را نگه دارد؟
main and topic each made an independent commit from B. Can main simply move its ref to topic and retain both histories?
راهنماییHint
ببین کدامیک نیاکان دیگری است.
Ask whether either tip is an ancestor of the other.
بیا بازش کنیمReasoning
نه، هر دو از B جدا شدهاند و هیچیک نیاکان دیگری نیست. برای نگهداشتن هر دو مسیر به نتیجهٔ ادغام و معمولاً یک merge commit با دو والد نیاز است.
No. Both diverged from B, so neither tip is an ancestor of the other. Preserving both lines requires a combined result and usually a two-parent merge commit.
git merge-base main topic یک شناسه شیء برگرداند. این شناسه شیء چه چیزی است و چه چیزی را ثابت نمیکند؟
git merge-base main topic returns an OID. What is it, and what does it not prove?
راهنماییHint
به نام فرمان و جایگاهش در گراف فکر کن.
Think about the command name and the graph.
راهحل و توضیحExplanation
یک بهترین commit مشترک برای دو تاریخچه است؛ شروع مقایسهٔ سهطرفه در این شکل ساده. بهتنهایی نمیگوید merge بدون تعارض میشود یا هر دو شاخه درستاند.
It is a best shared commit for the histories—the base for a three-way comparison in this simple graph. It does not say the merge will be conflict-free or that either branch is correct.
در شکل fast-forward، git merge --no-ff topic چه تفاوتی با merge عادی دارد؟
On a fast-forwardable graph, how does git merge --no-ff topic differ from a default merge?
راهنماییHint
چه چیزی در گراف اضافه میشود؟
What extra node appears in the graph?
چرا این جواب درست استAnswer
merge commit میسازد، حتی اگر main بتواند مستقیم جلو برود. این سیاست ثبت مرز ادغام است، نه الزام فنی و نه بهترین انتخاب همیشگی.
It creates a merge commit even though main could advance directly. This is a policy for recording an integration boundary, not a technical requirement or universal best practice.
git merge topic میگوید Already up to date. آیا merge شکست خورده؟
git merge topic says Already up to date. Did the merge fail?
راهنماییHint
آیا commit ورودی از قبل در رابطهٔ والدها شاخهٔ جاری هست؟
Is the incoming commit already an ancestor of the current branch?
بررسی جوابInterpretation
نه؛ چیزی برای افزودن وجود ندارد. اگر انتظار تغییر داشتی، شاخه جاری و نام topic را با git branch --show-current و گراف بررسی کن.
No; there is nothing to add. If you expected changes, check the current branch and topic name with git branch --show-current and the graph.
هر دو شاخه یک فایل را تغییر دادهاند، اما در خطهای جدا. آیا حتماً تعارض میشود؟
Both branches edited one file, but on separate lines. Must this conflict?
راهنماییHint
Git ناحیهٔ تغییر را نسبت به چه چیزی مقایسه میکند؟
Against what does Git compare each changed region?
پاسخ پیشنهادیPrecise answer
نه. اگر ناحیهها مستقل باشند، Git اغلب هر دو را ادغام میکند. بااینحال diff نهایی را بخوان؛ ادغام متنی نمیفهمد نتیجه از نظر معنا درست است.
No. If the regions are independent, Git often combines both. Still review the final diff; textual merging cannot know whether the result makes sense.
تا اینجا Git خودش بخش بزرگی از ادغام را انجام داده. از اینجا سؤال اصلی بیشتر انسانی میشود: اگر دو طرف نیت متفاوتی دارند، خروجی درست محصول کدام است؟ نشانگرهای تعارض فقط محل تصمیم را نشان میدهند.
So far Git has done much of the merge work itself. From here the main question becomes human: when the two sides express different intent, what should the product actually contain? Conflict markers only show where that decision is needed.
git ls-files -u برای یک فایل ناحیه آمادهسازیهای ۱، ۲ و ۳ را نشان میدهد. هرکدام چه نسخهای هستند؟
git ls-files -u shows stages 1, 2, and 3 for a path. Which versions are they?
راهنماییHint
base، شاخهٔ فعلی، شاخهٔ ورودی.
Base, current branch, incoming branch.
پاسخ و دلیلMapping
۱ = merge base؛ ۲ = ours، یعنی HEAD جاری در merge؛ ۳ = theirs، یعنی MERGE_HEAD. در modify/delete ممکن است یک ناحیه آمادهسازی غایب باشد.
1 = merge base; 2 = ours, HEAD in this merge; 3 = theirs, MERGE_HEAD. A modify/delete conflict may lack one stage.
در فایل، HEAD بالای ======= است. در این merge این نام به کدام سمت اشاره دارد؟
HEAD appears above =======. Which side is it in this merge?
راهنماییHint
کدام شاخه را قبل از اجرای merge checkout کردهای؟
Which branch was checked out before running merge?
بیا بازش کنیمAnswer
بالا ours است: شاخهٔ جاری و HEAD زمان merge. پایین theirs است: شاخه ورودی. این واژهها را به معنی «من» و «دیگری» در عملیات دیگر تعمیم نده.
The top is ours: the current branch and HEAD for this merge. The bottom is theirs: the incoming branch. Do not generalize these labels to “me/other” in different operations.
نشانگرها را در فایل حذف کردی، اما git status هنوز unmerged میگوید. چه مرحلهای جا مانده؟
You removed the markers, but git status still reports unmerged paths. What step remains?
راهنماییHint
فایل و ناحیه آمادهسازی یکی نیستند.
The file and index are separate.
راهحل و توضیحFix
فایل نهایی را بازبینی و با git add FILE ناحیه آمادهسازی کن. ویرایش پوشه کاری بهتنهایی ناحیه آمادهسازیهای unmerged ناحیه آمادهسازی را جایگزین نمیکند؛ بعد git ls-files -u را دوباره ببین.
Review the final file and stage it with git add FILE. Editing the working tree alone does not replace the index’s unmerged stages; rerun git ls-files -u.
ours میگوید «ثبتنام باز است»، theirs میگوید «ثبتنام بسته است»، و زمان انتشار هنوز معلوم نیست. آیا یکی را انتخاب میکنی؟
Ours says “registration open,” theirs says “registration closed,” and the release date is unknown. Do you choose one?
راهنماییHint
کدام مدرک Git ندارد؟
What evidence does Git lack?
چرا این جواب درست استResponsible decision
Git فقط دو نسخه و پایه را دارد؛ قصد کسبوکار را نمیداند. از صاحب نیاز یا قرارداد انتشار بپرس، یا merge را abort کن تا پاسخ روشن شود. انتخاب تصادفی یکی از sideها میتواند اطلاعات نادرست منتشر کند.
Git has the two versions and their base, not business intent. Ask the owner or check the release contract, or abort until you know. Randomly choosing a side could publish incorrect information.
گراف قابل fast-forward است. تیم میخواهد ادغام قابلیت بهشکل commit جدا در log بماند. کدام گزینه با این سیاست سازگار است؟
The graph is fast-forwardable. The team wants feature integration to remain an explicit commit in the log. Which option matches?
راهنماییHint
یکی از گزینهها merge commit را حتی در fast-forward میسازد.
One option creates a merge commit even when fast-forward is possible.
بررسی جوابAnswer and limit
git merge --no-ff feature. این یک سیاست برای شکل تاریخچه است؛ الزام فنی یا توصیهٔ همیشگی نیست و commit واقعی merge میسازد.
git merge --no-ff feature. This is a history policy, not a technical requirement or universal advice; it creates a real merge commit.
قبل از merge، ناحیه آمادهسازی تغییر ناحیه آمادهسازیشده و پوشه کاری هم فایل نیمهکاره دارد. امنترین قدم چیست؟
Before merging, the index has staged changes and the working tree has an unfinished file. What is the safest next step?
راهنماییHint
merge قرار است کدام تغییرها را ثبت کند؟
Which changes should the merge record?
پاسخ پیشنهادیReasoning
merge را شروع نکن. git status و git diff --staged را بخوان و تغییرها را جداگانه commit یا محفوظ کن. ناحیه آمادهسازی غیرتمیز ممکن است باعث توقف یا گرهخوردن کار نامرتبط شود؛ force راهحل نیست.
Do not start the merge. Read git status and git diff --staged, then commit or preserve the work separately. A dirty index can stop the merge or entangle unrelated work; forcing is not the fix.
در تمرینهای آخر، قبل از انتخاب ours یا theirs یا هر ویرایش دیگری، نسخهٔ پایه را هم ببین. خیلی وقتها جواب درست ترکیبی از هر دو طرف است، نه انتخاب کامل یکی.
In the final exercises, inspect the base before choosing ours, theirs, or another edit. The correct result is often a combination of both sides, not a wholesale choice of one.
در git ls-files -u برای policy.txt ناحیه آمادهسازی 1 و 2 هست، ناحیه آمادهسازی 3 نیست. چه اتفاقی محتمل است؟
git ls-files -u shows stages 1 and 2 for policy.txt, but no stage 3. What likely happened?
راهنماییHint
ناحیه آمادهسازی 3 همان incoming است.
Stage 3 is the incoming side.
پاسخ و دلیلInterpretation
در این جهت merge، ours فایل را نگه داشته/تغییر داده و theirs آن را حذف کرده است. ناحیه آمادهسازی غایب یعنی نسخهٔ آن سمت برای مسیر وجود ندارد؛ با git status جهت حذف/تغییر را تأیید کن.
In this merge direction, ours retained or modified the file while theirs deleted it. The missing stage means that side has no version for the path; confirm the direction with git status.
merge تعارض شده و میخواهی قبل از هر تصمیم به وضعیت قبل برگردی؛ قبل از merge پوشه بدون تغییر بوده. فرمان چیست و بعد چه چیزی را بررسی میکنی؟
A merge conflicted and you want to return to the pre-merge state; the tree was clean beforehand. What command do you use, and what do you verify?
راهنماییHint
لغو merge و بعد دوباره وضعیت.
Abort the merge, then inspect state again.
بیا بازش کنیمSafe path
git merge --abort؛ سپس git status --short --branch و گراف را بررسی کن. abort تلاش برای بازسازی است، نه تضمین برای تغییرهای محلی ناتمام قبل از merge.
git merge --abort, then inspect git status --short --branch and the graph. Abort attempts reconstruction; it is not a guarantee for unfinished local work present before merging.
فرمان git rev-list --parents -n 1 HEAD سه شناسه شیء چاپ میکند. چرا؟
git rev-list --parents -n 1 HEAD prints three OIDs. Why?
راهنماییHint
اولی خود commit است؛ چند تای بعدی والدها هستند.
The first is the commit; following IDs are parents.
راهحل و توضیحRead it
اولی شناسه خود HEAD و دو شناسه بعدی والدهای merge commit هستند: نوک قبلی شاخهٔ جاری و نوک شاخه ورودی. این شاهد merge commit بودن است؛ معنای فایلهای نهایی را نشان نمیدهد.
The first ID is HEAD; the next two are the merge commit’s parents: the former current tip and the incoming branch tip. This proves it is a merge commit, not what the resulting files mean.
پس از git add greeting.txt، git ls-files -u خالی است. آیا میتوانی merge را با اطمینان کامل commit کنی؟
After git add greeting.txt, git ls-files -u is empty. Can you commit the merge with complete confidence?
راهنماییHint
چه چیزی را ناحیه آمادهسازی نمیتواند دربارهٔ معنا بگوید؟
What can the index not tell you about meaning?
چرا این جواب درست استReview required
نه هنوز. این فقط میگوید مسیر از نظر Git unmerged نیست. git diff --cached، متن نهایی و نیاز محصول را بازبینی کن؛ سپس merge را ادامه بده.
Not yet. It only says Git no longer considers the path unmerged. Review git diff --cached, the final text, and product intent before continuing.
merge خودکار تمام شده اما برنامه بعد از آن رفتار نادرستی دارد؛ هر دو تغییر در فایلهای جدا بودند. آیا «بدون تعارض» یعنی «درست»؟ قدم تشخیصی چیست؟
An automatic merge completed, but the app behaves incorrectly afterward; the edits were in separate files. Does “no conflict” mean “correct”? What do you inspect?
راهنماییHint
merge متن را میسنجد، نه قرارداد برنامه را.
Merging checks text, not application contracts.
بررسی جوابIncident analysis
نه. Git توانست متن را کنار هم بگذارد، اما یکپارچگی معنایی را نمیفهمد. merge commit و diff را بخوان، آزمونهای مربوط را اجرا کن و تعامل دو تغییر را بررسی کن؛ «بدون تغییر merge» فقط نبود تعارض حلنشده است.
No. Git could combine text but cannot validate semantic integration. Inspect the merge commit and diff, run relevant tests, and check how the changes interact. A clean merge means no unresolved conflict, not correctness.
در main هستی. همکارت شاخه اشتباه را نام برده و merge با Already up to date تمام شده؛ انتظار تغییر داشتی. بهجای اجرای مجدد چند فرمان، چه شواهدی میگیری؟
You are on main. A teammate named the wrong branch; merge ended with “Already up to date,” though you expected changes. What evidence do you collect before retrying commands?
راهنماییHint
شاخه جاری، اشارهگرها و گراف را کنار هم ببین.
Inspect the current branch, refs, and graph together.
پاسخ پیشنهادیDiagnosis
با git branch --show-current جای خودت را تأیید کن؛ git branch -vv و git log --graph --oneline --decorate --all اسم و نوک شاخهها را نشان میدهند. بعد نام درست را انتخاب و merge کن. پیام قبلی میگوید همان ورودی از قبل در تاریخچه بوده، نه اینکه هیچ شاخه دیگری تغییر تازه ندارد.
Confirm your location with git branch --show-current; use git branch -vv and git log --graph --oneline --decorate --all to inspect names and tips. Then choose the correct branch and merge it. The earlier message means that input was already in history, not that no other branch has new work.
پروژهٔ کوچک: سه تصمیم برای یک محصولMini-project: three integration decisions for one product
سه شاخه، سه جور نتیجه
Three branches, three different outcomes
این پروژه سه نوع ادغام جلویت میگذارد: یکی که Git خودش انجام میدهد، یکی که متن با متن درگیر میشود و یکی که باید دربارهٔ حذف یا نگهداشتن فایل تصمیم بگیری. هدف این نیست که نشانگرهای تعارض را پاک کنی؛ هدف این است که نتیجهٔ نهایی واقعاً همان چیزی باشد که محصول میخواهد.
Your team is working on product documentation. Automatically integrate a clean change, resolve a text conflict with explicit intent, and decide whether a policy document survives a modify/delete conflict. Do not “solve” any of them by blindly deleting markers.
پرونده را جدا بساز
Create the case in isolation
این فرمانها فقط پوشهٔ آزمایشی تازه را تغییر میدهند. اگر از قبل چنین پوشهای داری، نام دیگری انتخاب کن.
These commands change only a new disposable folder. If that name already exists, choose another.
mkdir merge-project && cd merge-project git init -b main git config user.name 'Nika Example' git config user.email 'nika@example.test' printf 'سلام به تیم\n' > greeting.txt printf 'قانون نسخه: پایدار بماند\n' > policy.txt printf 'شروع\n' > README.txt git add . git commit -m 'Create product docs'
تصمیم اول: فایلهای جدا، merge خودکار
Decision 1: separate files, clean merge
از main دو شاخه مستقل بساز؛ یکی changelog و دیگری راهنمای سریع اضافه کند. هر دو از یک پایهاند. merge اول را بررسی کن، سپس دومی را merge کن و ببین چرا بار دوم ممکن است merge commit بسازد، هرچند تعارضی نیست.
Create two independent branches from main: one adds a changelog, the other a quick guide. Both start at the same base. Inspect the first merge, then merge the second and explain why it may create a merge commit even though there is no conflict.
git switch -c feature/changelog printf 'v1: انتشار آزمایشی\n' > CHANGELOG.txt git add CHANGELOG.txt && git commit -m 'Add changelog' git switch main git switch -c feature/quickstart printf '۱. نصب\n۲. اجرا\n' > QUICKSTART.txt git add QUICKSTART.txt && git commit -m 'Add quickstart' git switch main git merge feature/changelog git log --oneline --graph --decorate --all git merge feature/quickstart git rev-list --parents -n 1 HEAD git log --oneline --graph --decorate --all
تصمیم دوم: یک خط، دو خواسته
Decision 2: one line, two intentions
دو شاخه تازه از main بسازند: یکی پیام را برای تیم محصول شخصی کند و دیگری برای تیم مهندسی. بعد از merge اول، merge دوم را اجرا کن. قبل از ویرایش، در دفترچه بنویس چه جملهای مخاطب هر دو را درست پوشش میدهد؛ بعد نشانگرها را بردار، متن را بازبینی و ناحیه آمادهسازی کن.
Create two new branches from main: one personalizes the greeting for product, the other for engineering. After the first merge, run the second. Before editing, write down a sentence that correctly addresses both audiences; then remove markers, review, and stage it.
git switch -c feature/product-greeting printf 'سلام به تیم محصول\n' > greeting.txt git add greeting.txt && git commit -m 'Address product team' git switch main git switch -c feature/engineering-greeting printf 'سلام به تیم مهندسی\n' > greeting.txt git add greeting.txt && git commit -m 'Address engineering team' git switch main git merge feature/product-greeting git merge feature/engineering-greeting git status git ls-files -u git show :1:greeting.txt git show :2:greeting.txt git show :3:greeting.txt
نتیجهٔ پیشنهادی: سلام به تیم محصول و مهندسی. این تنها جواب ممکن نیست؛ اگر محصول قرارداد لحن دیگری دارد، همان را انتخاب کن و دلیلش را ثبت کن.
One reasonable result is سلام به تیم محصول و مهندسی. It is not the only valid answer; if the product has another tone contract, choose it and record why.
printf 'سلام به تیم محصول و مهندسی\n' > greeting.txt git diff --check git add greeting.txt git diff --cached git merge --continue git status --short --branch git log --oneline --graph --decorate --all
تصمیم سوم: سند تغییر کرد یا حذف شد؟
Decision 3: update or delete the policy?
از main، شاخهی بساز که سیاست را دقیقتر میکند؛ از main دیگری بساز که همان سیاست را حذف میکند. بعد یکی را merge و دیگری را اجرا کن. این پرونده تصمیم محصول میخواهد، نه مقایسهٔ دو تکه متن؛ سیاست انتشار باید بماند، پس نسخهٔ اصلاحشده را نگه دار.
From main, create one branch that clarifies the policy and another that deletes it. Merge one, then attempt the other. This needs a product decision, not a text comparison: the release policy must remain, so keep the revised version.
git switch -c feature/policy-update printf 'قانون نسخه: پایدار بماند؛ انتشار فقط پس از بازبینی\n' > policy.txt git add policy.txt && git commit -m 'Clarify release policy' git switch main git switch -c feature/remove-old-policy git rm policy.txt git commit -m 'Remove obsolete policy' git switch main git merge feature/policy-update git merge feature/remove-old-policy git status git ls-files -u git show :1:policy.txt git show :2:policy.txt
ناحیه آمادهسازی 3 برای نسخهٔ حذفشده وجود ندارد. تصمیم پروژه نگهداشتن سیاست است؛ فایل را بازبینی کن و با git add policy.txt ناحیه آمادهسازی کن، سپس merge را ادامه بده. اگر تصمیم واقعی تیم حذف بود، باید git rm policy.txt میزدی. در دفترچه بنویس چه مدرکی Git داد، چه چیزی را نمیدانست و انسان چه تصمیمی گرفت.
Stage 3 is absent for the deleted version. The project decision is to retain the policy: review it and stage it with git add policy.txt, then continue the merge. If the team's real decision were deletion, you would use git rm policy.txt. Record what Git showed, what it could not know, and what the human decided.
git add policy.txt git diff --cached git merge --continue git status --short --branch git log --oneline --graph --decorate --all git show HEAD:policy.txt
راهنمایی اگر گراف گیجکننده شدHint if the graph gets confusing
در هر تعارض، شاخه جاری را با git branch --show-current پیدا کن. اسم شاخه ورودی را از فرمان merge بردار؛ مثلاً در تعارض دوم پروژه git merge-base HEAD feature/engineering-greeting و git log --graph --oneline --decorate --all را بخوان. برای هر فایل فقط وقتی همهٔ نیازها را فهمیدی تصمیم بگیر.
For each conflict, identify the current branch with git branch --show-current. Use the incoming branch name from your merge command; for the second project conflict, for example, run git merge-base HEAD feature/engineering-greeting, then inspect git log --graph --oneline --decorate --all. Decide on each file only after you understand the requirements.
برای هر سه ادغام ثبت کن: Git چه چیزی را خودکار فهمید؟ کدام بخشِ معنا را نمیدانست؟ چه تصمیم انسانی نتیجه را تعیین کرد؟ اگر بتوانی جوابها را از روی گراف، ناحیه آمادهسازی و diff نشان بدهی، پروژه کامل است.
For each of the three integrations, record: What could Git infer automatically? What meaning did it lack? Which human decision determined the result? If you can support your answers with the graph, index, and diff, the project is complete.
merge دو خط تاریخ را نگه میدارد؛ قدم بعدی چه میشود؟Merge preserves both lines—what comes next?
merge هر دو مسیر تاریخ را نگه میدارد و یک نقطهٔ اتصال میسازد. فصل بعد راه دیگری را میبیند: تغییرهای یک مسیر را دوباره روی پایهٔ تازه میسازیم؛ کاری که ظاهر گراف را عوض میکند و شناسهٔ commitها را هم تغییر میدهد.
You can now distinguish a fast-forward from a merge commit, locate the merge base, and read a conflict as an unresolved decision rather than Git malfunction. A merge joins two lines of history with a multi-parent commit.
فصل ۰۷ سؤال تازهای میپرسد: اگر بهجای وصلکردن دو مسیر، commitهای خودمان را روی پایهٔ جدید دوباره اجرا کنیم چه میشود؟ جواب، rebase است—و چون commit از والدش شناسه میگیرد، شناسهها عوض میشوند.
Chapter 07 asks a different question: what if instead of joining two lines, we replay our commits onto a new base? That is rebase—and because a commit’s ID depends on its parent, the IDs change.
نقشهٔ سریع mergeQuick merge reference
| اگر این را میبینی…If you see… | بررسی کن…Check… | یادت بماند…Remember… |
|---|---|---|
Already up to date | git log --graph --oneline --decorate --all | خطا نیست؛ ورودی احتمالاً از قبل در رابطهٔ والدها هست.Not an error; the input is probably already an ancestor. |
Fast-forward | git rev-parse HEAD topic | اشارهگر حرکت میکند؛ merge commit ساخته نمیشود.The ref moves; no merge commit is created. |
CONFLICT | git status · git ls-files -u | قصد را مشخص کن، بعد فایل را ناحیه آمادهسازی کن.Determine intent, then stage the result. |
| حل شد؟Resolved? | git diff --cached · git merge --continue | خالیشدن ناحیه آمادهسازیها، درستی معنایی را ثابت نمیکند.No unmerged stages does not prove semantic correctness. |
| نمیخواهی ادامه بدهیNeed to back out? | git merge --abort | بازسازی تلاش میشود؛ کار dirty قبلی تضمیناً برنمیگردد.Reconstruction is attempted; prior dirty work is not guaranteed. |
- fast-forward فقط اشارهگر را جلو میبرد؛ تاریخچهٔ تازه نمیسازد.
- A fast-forward moves a ref; it creates no new history node.
- merge commit دو والد دارد و دو مسیر را به هم وصل میکند.
- A merge commit has two parents and joins two lines.
- تعارض یعنی Git شواهد دارد اما قصد نهایی را نمیداند؛ تصمیم و بازبینی با انسان است.
- A conflict means Git has evidence but lacks intent; a person decides and reviews.