توی این درس یاد میگیری وقتی دو نفر یک جای فایل را عوض کردهاند و Git نمیداند کدام درست است (یک conflict)، آرام و قدمبهقدم حلش کنی. علامتهای <<<<<<< و ======= و >>>>>>> را میخوانی، فایل را درست میکنی، با git add و git commit ادغام را تمام میکنی، با git checkout --ours/--theirs یکی از دو طرف را برمیداری، و اگر پشیمان شدی با git merge --abort برمیگردی. تمرین: یک conflict عمدی بساز و حلش کن.
مسئله: Git هم نمیتواند حدس بزند
Section titled “مسئله: Git هم نمیتواند حدس بزند”در درس قبل دیدی Git دو شاخه را خودکار ادغام میکند، به شرط اینکه جاهای مختلفی از فایل را عوض کرده باشند. ولی اگر هر دو یک خط را عوض کنند، یکی میگوید «قیمت ۶» و دیگری «قیمت ۷»؛ Git دلیلی ندارد یکی را ترجیح دهد. در این حالت متوقف میشود و میگوید «تو تصمیم بگیر». اسم این وضعیت conflict (تضاد) است؛ ترسناک نیست و نشانهی خرابی هم نیست: فقط یعنی یک تصمیم انسانی لازم است.
تشبیه: دو ویراستار، یک جمله
Section titled “تشبیه: دو ویراستار، یک جمله”دو ویراستار یک جملهی کتاب را جداگانه بازنویسی کردهاند. سردبیر هر دو را کنار هم میبیند و باید تصمیم بگیرد: نسخهی اول، نسخهی دوم، یا ترکیبی. Git همان سردبیر است که فقط هر دو نسخه را نشان میدهد؛ انتخاب با توست. بخشهایی از متن که هر دو ویراستار دست نزدهاند یا فقط یکی عوض کرده، بدون سؤال ادغام میشوند.
مثالهای عملی
Section titled “مثالهای عملی”فایل سادهی منوی یک کافه با سه خط. از یک commit مشترک دو شاخه جدا میکنم و هر دو را تغییر میدهم:
مثال ۱: ساختن conflict و دیدن آن
Section titled “مثال ۱: ساختن conflict و دیدن آن”mkdir -p ~/gitlab/cafe && cd ~/gitlab/cafe && git init -qprintf 'Name: Cafe\nCoffee: 5\nOpen: 9-17\n' > menu.txtgit add menu.txt && git commit -qm "Add menu"git switch -qc feature/pricesed -i 's/^Coffee: 5/Coffee: 6/; s/^Open: 9-17/Open: 8-18/' menu.txtgit commit -qam "Raise coffee price and open earlier"git switch -q mainsed -i 's/^Name: Cafe/Name: Cafe Loop/; s/^Coffee: 5/Coffee: 7/' menu.txtgit commit -qam "Rename cafe and set coffee price to 7"echo "=== ادغام:"git merge feature/priceecho "=== وضعیت:"git status=== ادغام:Auto-merging menu.txtCONFLICT (content): Merge conflict in menu.txtAutomatic merge failed; fix conflicts and then commit the result.=== وضعیت:On branch mainYou have unmerged paths. (fix conflicts and run "git commit") (use "git merge --abort" to abort the merge)
Unmerged paths: (use "git add <file>..." to mark resolution) both modified: menu.txt
no changes added to commit (use "git add" and/or "git commit -a")دو چیز را دقیق ببین: Git خودش همهی ادغامهای بیتضاد را انجام داد (Auto-merging: نام کافه از main و ساعت کار از feature آمد)، و فقط روی یک خط (Coffee) متوقف شد. git status حالا فایل را زیر Unmerged paths و با both modified نشان میدهد؛ یعنی «هر دو طرف عوضش کردهاند». و ادغام نیمهکاره است: نه تمام شده و نه لغو.
مثال ۲: خواندن علامتها
Section titled “مثال ۲: خواندن علامتها”Git داخل خود فایل علامت گذاشته. فایل را ببین:
cd ~/gitlab/cafecat menu.txt<<<<<<< HEADName: Cafe LoopCoffee: 7Open: 9-17=======Name: CafeCoffee: 6Open: 8-18>>>>>>> feature/priceعلامتها اینطورند:
| بخش | معنی |
|---|---|
<<<<<<< HEAD |
شروع قسمت تضاددار؛ از اینجا تا =======، نسخهی شاخهی فعلی (HEAD، همان main) |
======= |
جداکنندهی دو نسخه |
>>>>>>> feature/price |
از ======= تا اینجا، نسخهی شاخهی ادغامشده (feature/price) |
بخشهای بدون علامت (Name و Open) همانهاییاند که Git خودکار ادغام کرد. تو باید این سه بخش (دو نسخه و علامتها) را با یک نسخهی نهایی عوض کنی. دستور git diff هم در این وضعیت یک شکل ویژه نشان میدهد (diff ترکیبی با دو ستون علامت، یکی برای هر والد):
cd ~/gitlab/cafegit diffdiff --cc menu.txtindex 9ae8221,827f826..0000000--- a/menu.txt+++ b/menu.txt@@@ -1,3 -1,3 +1,9 @@@++<<<<<<< HEAD +Name: Cafe Loop +Coffee: 7 +Open: 9-17++=======+ Name: Cafe+ Coffee: 6+ Open: 8-18++>>>>>>> feature/priceمثال ۳: حل دستی، edit ← add ← commit
Section titled “مثال ۳: حل دستی، edit ← add ← commit”حالا تصمیم میگیرم: قیمت را ۶٫۵ میگذارم (بین دو پیشنهاد) و ساعت کار و نام کافه همان ادغامشده. در واقعیت فایل را با ویرایشگر باز میکنی و علامتها را پاک میکنی؛ اینجا برای اینکه قابلاجرا باشد، محتوای نهایی فایل را مینویسم:
cd ~/gitlab/cafecat > menu.txt <<'EOF'Name: Cafe LoopCoffee: 6.5Open: 8-18EOFecho "--- بعد از حل، فایل:"cat menu.txtgit add menu.txtecho "--- وضعیت بعد از add (حل شد، منتظر commit):"git status | head -6git commit --no-editecho "--- گراف:"git log --oneline --graph--- بعد از حل، فایل:Name: Cafe LoopCoffee: 6.5Open: 8-18--- وضعیت بعد از add (حل شد، منتظر commit):On branch mainAll conflicts fixed but you are still merging. (use "git commit" to conclude merge)
Changes to be committed: modified: menu.txt[main dc4ad5a] Merge branch 'feature/price'--- گراف:* dc4ad5a Merge branch 'feature/price'|\| * 4f25ba3 Raise coffee price and open earlier* | 488713f Rename cafe and set coffee price to 7|/* 9476ac5 Add menuسه قدم: ۱) فایل را درست کن (فقط متن نهایی؛ هیچ علامتی نماند)، ۲) git add فایل یعنی «این فایل حل شد»، ۳) git commit ادغام را تمام میکند (--no-edit پیام پیشفرض Merge branch... را نگه میدارد؛ میتوانی git merge --continue هم بزنی). حالا commit ادغام با دو والد ساخته شد.
مثال ۴: پشیمان شدی؟ git merge --abort
Section titled “مثال ۴: پشیمان شدی؟ git merge --abort”وسط حل، اگر گیج شدی یا فهمیدی ادغام را در لحظهی بد شروع کردهای، یک دستور همهچیز را به قبل از merge برمیگرداند:
cd ~/gitlab/cafegit switch -qc feature/menu2sed -i 's/^Coffee: 6.5/Coffee: 8/' menu.txtgit commit -qam "Coffee price 8 on feature"git switch -q mainsed -i 's/^Coffee: 6.5/Coffee: 9/' menu.txtgit commit -qam "Coffee price 9 on main"echo "=== ادغام با conflict:"git merge feature/menu2 2>&1 | tail -2git status -secho "=== لغو:"git merge --abortgit status -secho "(خالی: همهچیز مثل قبل از merge)"cat menu.txt=== ادغام با conflict:CONFLICT (content): Merge conflict in menu.txtAutomatic merge failed; fix conflicts and then commit the result.UU menu.txt=== لغو:(خالی: همهچیز مثل قبل از merge)Name: Cafe LoopCoffee: 9Open: 8-18--abort فقط تا قبل از commit کردن ادغام کار میکند و دقیقاً همان وضعیت قبل از git merge را برمیگرداند؛ هیچ ردی نمیماند. بیخطرترین راه برای «بیا دوباره شروع کنیم» است.
مثال ۵: یک طرف را کامل برداشتن، --ours و --theirs
Section titled “مثال ۵: یک طرف را کامل برداشتن، --ours و --theirs”گاهی نیازی به ترکیب نیست؛ فقط میخواهی نسخهی یکی از دو طرف را بپذیری. در ادغام، ours شاخهی فعلی (جایی که merge را زدی) و theirs شاخهی ادغامشده است:
cd ~/gitlab/cafegit merge feature/menu2 2>&1 | tail -1echo "--- نسخهی خودم (ours = main):"git checkout --ours menu.txtgrep Coffee menu.txtecho "--- نسخهی آنها (theirs = feature/menu2):"git checkout --theirs menu.txtgrep Coffee menu.txtgit add menu.txtgit commit -q --no-editgit log --oneline -1Automatic merge failed; fix conflicts and then commit the result.--- نسخهی خودم (ours = main):Updated 1 path from the indexCoffee: 9--- نسخهی آنها (theirs = feature/menu2):Updated 1 path from the indexCoffee: 8c88049e Merge branch 'feature/menu2'git checkout --ours فایل / --theirs فایل فایل را کاملاً با نسخهی آن طرف عوض میکند (دستور همارز جدید: git restore --ours/--theirs فایل). بعدش مثل همیشه add و commit. هشدار: این همهی فایل را از یک طرف میگیرد، نه فقط بخش تضاددار (تغییرهای بیتضاد آن طرف دیگر هم از دست میرود). برای انتخاب فقط در بخشهای تضاددار، گزینهی استراتژی git merge -X ours یا -X theirs هست؛ ولی بیصدا تصمیم میگیرد و خطرناک است، پس فقط وقتی ازش مطمئنی.
مثال ۶: انواع conflict، وقتی یک طرف فایل را حذف کرده
Section titled “مثال ۶: انواع conflict، وقتی یک طرف فایل را حذف کرده”همهی conflict ها «دو نفر یک خط» نیستند. اگر یکی فایل را ویرایش کند و دیگری پاک، Git نمیداند فایل باید بماند یا برود:
mkdir -p ~/gitlab/del && cd ~/gitlab/del && git init -qecho "v1" > notes.txt && git add notes.txt && git commit -qm "Add notes"git switch -qc editecho "v2 edited" > notes.txt && git commit -qam "Edit notes"git switch -q maingit rm -q notes.txt && git commit -qm "Delete notes"echo "=== ادغام:"git merge edit 2>&1 | tail -3echo "=== وضعیت (DU = من حذف کردهام، آنها ویرایش):"git status -secho "=== تصمیم: فایل را نگه میدارم (نسخهی ویرایششده):"git add notes.txtgit commit -q --no-editls; cat notes.txt=== ادغام:CONFLICT (modify/delete): notes.txt deleted in HEAD and modified in edit. Version edit of notes.txt left in tree.Automatic merge failed; fix conflicts and then commit the result.=== وضعیت (DU = من حذف کردهام، آنها ویرایش):DU notes.txt=== تصمیم: فایل را نگه میدارم (نسخهی ویرایششده):notes.txtv2 editedحروف DU را از چپ بخوان: ستون اول وضعیت ours (D = حذف کردهایم)، ستون دوم theirs (U = updated: آنها ویرایش کردهاند). تصمیم تو: اگر میخواهی فایل برود git rm notes.txt، اگر میخواهی بماند git add notes.txt. حروف دیگر: UU هر دو ویرایش، AA هر دو فایلی با همین نام ساختهاند، UD برعکس DU.
مثال ۷: دیدن «نسخهی اصلی» با diff3 و zdiff3
Section titled “مثال ۷: دیدن «نسخهی اصلی» با diff3 و zdiff3”علامتهای پیشفرض فقط دو نسخه را نشان میدهند. گاهی برای تصمیمگیری باید بدانی «اول چه بود». با merge.conflictStyle یک بخش سوم هم میآید: نسخهی جدّ مشترک (base):
cd ~/gitlab/cafegit config merge.conflictStyle zdiff3git switch -qc feature/zdsed -i 's/^Coffee: 8/Coffee: 12/' menu.txtgit commit -qam "Coffee 12 on feature"git switch -q mainsed -i 's/^Coffee: 8/Coffee: 10/' menu.txtgit commit -qam "Coffee 10 on main"git merge feature/zd 2>&1 | tail -1cat menu.txtgit merge --abortAutomatic merge failed; fix conflicts and then commit the result.Name: Cafe Loop<<<<<<< HEADCoffee: 10||||||| c88049eCoffee: 8=======Coffee: 12>>>>>>> feature/zdOpen: 8-18حالا بین دو علامت اصلی یک خط ||||||| با نسخهی قبل از هر دو تغییر (Coffee: 8) هم هست. با دیدن اینکه از چه عددی شروع شد، معلوم میشود هر نفر چه کرده (یکی ۸ را به ۱۰ برده و دیگری به ۱۲). برای همیشه روشنش کن: git config --global merge.conflictStyle zdiff3 (از Git ۲.۳۵ به بعد؛ diff3 قدیمیتر است ولی همان کار را میکند). بسیاری از حرفهایها همین را پیشفرض میکنند.
مثال ۸: ابزارهای کمکی
Section titled “مثال ۸: ابزارهای کمکی”ویرایش دستی علامتها همیشه لازم نیست. دو ابزار رایج:
git mergetool: یک ابزار گرافیکی ادغام (meld،kdiff3،vimdiff، VS Code…) باز میکند که سه ستون (ours، base، theirs) و یک نتیجه نشان میدهد. فهرست ابزارهایی که روی این ماشین هستند:
git mergetool --tool-help 2>&1 | head -12'git mergetool --tool=<tool>' may be set to one of the following: vimdiff Use Vim with a custom layout (see `git help mergetool`'s `BACKEND SPECIFIC HINTS` section) vimdiff1 Use Vim with a 2 panes layout (LOCAL and REMOTE) vimdiff2 Use Vim with a 3 panes layout (LOCAL, MERGED and REMOTE) vimdiff3 Use Vim where only the MERGED file is shown
The following tools are valid, but not currently available: araxis Use Araxis Merge (requires a graphical session) bc Use Beyond Compare (requires a graphical session) bc3 Use Beyond Compare (requires a graphical session) bc4 Use Beyond Compare (requires a graphical session) codecompare Use Code Compare (requires a graphical session)- ویرایشگرهای مدرن (مثل VS Code و JetBrains) دکمههای «Accept Current / Accept Incoming / Accept Both» بالای هر بخش تضاددار دارند و ویرایشگر ادغام سهستونه. این را من اجرا نکردهام (رابط گرافیکی دارد)؛ ولی کار همان است که دستی کردیم: نسخهی نهایی را بنویس،
git addوgit commit.
مثال ۹: یادآور حلها، rerere
Section titled “مثال ۹: یادآور حلها، rerere”وقتی یک conflict را حل میکنی و بعداً همان را دوباره میبینی (مثلاً ادغام را لغو میکنی و دوباره شروع میکنی، یا در rebase)، دوباره حلش کردن خستهکننده است. rerere (reuse recorded resolution) راهحلت را بهخاطر میسپارد:
mkdir -p ~/gitlab/rr && cd ~/gitlab/rr && git init -qgit config rerere.enabled trueecho "price: 5" > p.txt && git add p.txt && git commit -qm "Base"git switch -qc feature && echo "price: 6" > p.txt && git commit -qam "Feature price"git switch -q main && echo "price: 7" > p.txt && git commit -qam "Main price"echo "=== بار اول: conflict، و حل دستی:"git merge feature 2>&1 | tail -1echo "price: 6.5" > p.txt && git add p.txt && git commit -q --no-editecho "=== ادغام را برمیگردانم (فقط برای آزمایش) و دوباره میزنم:"git reset -q --hard HEAD~1git merge feature 2>&1 | tail -3echo "--- فایل (Git خودش حل را دوباره اعمال کرد، ولی هنوز staged نیست):"cat p.txtgit status -s=== بار اول: conflict، و حل دستی:Automatic merge failed; fix conflicts and then commit the result.Recorded resolution for 'p.txt'.=== ادغام را برمیگردانم (فقط برای آزمایش) و دوباره میزنم:CONFLICT (content): Merge conflict in p.txtResolved 'p.txt' using previous resolution.Automatic merge failed; fix conflicts and then commit the result.--- فایل (Git خودش حل را دوباره اعمال کرد، ولی هنوز staged نیست):price: 6.5UU p.txtResolved 'p.txt' using previous resolution: Git حل قبلیات را اعمال کرد (باید فقط git add و commit بزنی). برای فعالکردن دائمی: git config --global rerere.enabled true. در تیمهایی که زیاد rebase میکنند خیلی کمک میکند.
پشت پرده: Git conflict را چطور تشخیص میدهد؟
Section titled “پشت پرده: Git conflict را چطور تشخیص میدهد؟”ادغام سهطرفه سه نسخه از هر فایل را مقایسه میکند: base (جدّ مشترک)، ours و theirs. اگر فقط یک طرف نسبت به base عوض کرده، آن را میپذیرد؛ اگر هر دو یک جای متن را عوض کرده باشند (و متفاوت)، conflict. در حالت تضاد، Git سه نسخهی فایل را در ناحیهی staging نگه میدارد (slot های ۱، ۲ و ۳) تا بتوانی هر کدام را بخوانی. ببین:
mkdir -p ~/gitlab/stages && cd ~/gitlab/stages && git init -qecho "x: 1" > f.txt && git add f.txt && git commit -qm "Base"git switch -qc b && echo "x: 2" > f.txt && git commit -qam "B"git switch -q main && echo "x: 3" > f.txt && git commit -qam "A"git merge b > /dev/null 2>&1echo "--- ناحیهی staging در حال conflict (۱=base، ۲=ours، ۳=theirs):"git ls-files -uecho "--- محتوای هر سه:"for n in 1 2 3; do printf "stage $n: "; git show :$n:f.txt; done--- ناحیهی staging در حال conflict (۱=base، ۲=ours، ۳=theirs):100644 d508cf756344aaf9123c531122a9d2d410c4ecfa 1 f.txt100644 e5cfddc4f7c407e08d3b6ba2bfdeec91b849c780 2 f.txt100644 ad932ccf517a09d23b7b892d6ee994bebdb7e8da 3 f.txt--- محتوای هر سه:stage 1: x: 1stage 2: x: 3stage 3: x: 2عدد بعد از حالت (۱ و ۲ و ۳) شمارهی slot است: ۱ = base، ۲ = ours، ۳ = theirs؛ و git show :۲:فایل هر کدام را چاپ میکند. بعد از git add، این سه slot با یک نسخهی حلشده (slot ۰) جایگزین میشود و Git میداند حل شد. به همین دلیل git add همان «علامت حلشدن» است.
جدولهای مرجع
Section titled “جدولهای مرجع”| دستور | کار |
|---|---|
git merge شاخه |
شروع ادغام (ممکن است conflict بدهد) |
git status / git status -s |
فایلهای تضاددار (UU، DU، UD، AA) |
git diff |
diff ترکیبی در حال conflict |
git add فایل |
فایل حل شد |
git commit / git merge --continue |
پایان ادغام |
git merge --abort |
لغو و برگشت به قبل از merge |
git checkout --ours فایل / --theirs |
نسخهی یک طرف |
git config merge.conflictStyle zdiff3 |
نمایش base هم |
git config rerere.enabled true |
یادآور حلها |
git diff --check |
یافتن علامتهای conflict جامانده |
git mergetool |
ابزار گرافیکی |
| وضعیت | معنی |
|---|---|
UU |
هر دو ویرایش کردهاند (تضاد محتوا) |
AA |
هر دو فایلی با همین نام ساختهاند |
DU |
ما حذف کردهایم، آنها ویرایش |
UD |
ما ویرایش کردهایم، آنها حذف |
اشتباهات رایج
Section titled “اشتباهات رایج”۱) commit قبل از حل
Section titled “۱) commit قبل از حل”mkdir -p ~/gitlab/early && cd ~/gitlab/early && git init -qecho "n: 1" > f.txt && git add f.txt && git commit -qm "Base"git switch -qc b && echo "n: 2" > f.txt && git commit -qam "B"git switch -q main && echo "n: 3" > f.txt && git commit -qam "A"git merge b > /dev/null 2>&1git commit -m "try to commit" 2>&1 | head -4error: Committing is not possible because you have unmerged files.hint: Fix them up in the work tree, and then use 'git add/rm <file>'hint: as appropriate to mark resolution and make a commit.fatal: Exiting because of an unresolved conflict.Committing is not possible because you have unmerged files: Git جلوی commit را میگیرد تا conflict ناتمام ثبت نشود. راهحل: فایل را حل و git add کن.
۲) علامتها در فایل جا میمانند
Section titled “۲) علامتها در فایل جا میمانند”بدترین حالت: علامتها را پاک نمیکنی، git add میزنی (که فقط «حل شد» را علامت میزند) و commit میکنی. Git هیچچیز نمیگوید و فایلی با <<<<<<< وارد تاریخچه میشود (و کد یا تنظیمات میشکند). راه گرفتنش git diff --check است:
cd ~/gitlab/earlygit add f.txtecho "--- علامتها هنوز در فایلاند؛ git diff --check چه میگوید؟"git diff --cached --check; echo "کد خروج: $?"echo "--- و جستجوی دستی:"grep -n '^[<=>]\{7\}' f.txtgit merge --abort--- علامتها هنوز در فایلاند؛ git diff --check چه میگوید؟f.txt:1: leftover conflict markerf.txt:3: leftover conflict markerf.txt:5: leftover conflict markerکد خروج: 2--- و جستجوی دستی:1:<<<<<<< HEAD3:=======5:>>>>>>> bgit diff --check (برای staging: --cached) خطهایی با علامت conflict و فاصلهی اضافه را گزارش میدهد و کد خروج غیرصفر برمیگرداند. قبل از commit آن را بزن (یا در یک pre-commit hook بگذار). و بعد از هر حل، پروژه را اجرا و تست کن؛ فایلی که علامت ندارد ولی منطقش اشتباه ادغام شده هم ممکن است.
۳) انتخاب کورکورانهی یک طرف
Section titled “۳) انتخاب کورکورانهی یک طرف”--ours یا -X theirs را بدون خواندن میزنی و کار همکارت بیصدا از بین میرود. راهحل: هر conflict را با git diff بخوان؛ اگر نمیدانی کدام درست است، از همکارت بپرس.
۴) حل conflict روی شاخهی اشتباه
Section titled “۴) حل conflict روی شاخهی اشتباه”مثل هر merge، قبل از شروع ببین روی کدام شاخهای (git branch). ادغام در جهت اشتباه، ours و theirs را عوض میکند و ممکن است نسخهی اشتباه برنده شود.
۵) ترس و لغو نکردن
Section titled “۵) ترس و لغو نکردن”اگر وسط حل گیر کردی، git merge --abort بدون هیچ ضرری برمیگرداند. بعد با خیال راحت دوباره شروع کن.
یک conflict بساز و بلافاصله با git merge --abort آن را لغو کن؛ ثابت کن مخزن به حالت قبل برگشت.
دیدن جواب
mkdir -p ~/gitlab/ex1 && cd ~/gitlab/ex1 && git init -qecho "a" > f && git add f && git commit -qm "Base"git switch -qc x && echo "b" > f && git commit -qam "X"git switch -q main && echo "c" > f && git commit -qam "Y"git merge x 2>&1 | tail -1git status -sgit merge --abortgit status -s; echo "(خالی)"cat fAutomatic merge failed; fix conflicts and then commit the result.UU f(خالی)cتمرین اصلی: یک conflict عمدی بساز و حلش کن. فایل config.ini با خط timeout=30 را در دو شاخه به دو مقدار متفاوت عوض کن و در main ادغام کن؛ مقدار نهایی را (timeout=45) خودت انتخاب کن و ادغام را تمام کن. ثابت کن در فایل هیچ علامتی نمانده.
دیدن جواب
mkdir -p ~/gitlab/ex2 && cd ~/gitlab/ex2 && git init -qprintf 'name=app\ntimeout=30\n' > config.ini && git add config.ini && git commit -qm "Add config"git switch -qc feature/fast && sed -i 's/timeout=30/timeout=10/' config.ini && git commit -qam "Shorter timeout"git switch -q main && sed -i 's/timeout=30/timeout=60/' config.ini && git commit -qam "Longer timeout"git merge feature/fast 2>&1 | tail -2echo "--- حل: مقدار نهایی را من مینویسم"printf 'name=app\ntimeout=45\n' > config.inigit add config.ini && git commit -q --no-editecho "--- بررسی علامتها:"grep -c '^[<=>]\{7\}' config.ini || echo "هیچ علامتی نمانده"cat config.inigit log --oneline --graphCONFLICT (content): Merge conflict in config.iniAutomatic merge failed; fix conflicts and then commit the result.--- حل: مقدار نهایی را من مینویسم--- بررسی علامتها:0هیچ علامتی نماندهname=apptimeout=45* 1406b70 Merge branch 'feature/fast'|\| * 2558f81 Shorter timeout* | cba8e25 Longer timeout|/* 7c9a0c0 Add configیک conflict از نوع حذف و ویرایش بساز (یک شاخه فایل report.txt را ویرایش میکند، دیگری پاکش میکند) و آن را طوری حل کن که فایل پاک شود (تصمیم: دیگر لازم نیست). با git status -s نوع conflict را نشان بده و در پایان ثابت کن فایل در پروژه نیست.
دیدن جواب
mkdir -p ~/gitlab/ex3 && cd ~/gitlab/ex3 && git init -qecho "draft" > report.txt && echo keep > keep.txt && git add . && git commit -qm "Add files"git switch -qc edit && echo "draft v2" > report.txt && git commit -qam "Edit report"git switch -q main && git rm -q report.txt && git commit -qm "Remove report"git merge edit 2>&1 | tail -2echo "--- نوع conflict:"git status -secho "--- تصمیم: حذف (git rm)"git rm -q report.txtgit commit -q --no-editlsgit log --oneline --graphCONFLICT (modify/delete): report.txt deleted in HEAD and modified in edit. Version edit of report.txt left in tree.Automatic merge failed; fix conflicts and then commit the result.--- نوع conflict:DU report.txt--- تصمیم: حذف (git rm)keep.txt* cdc55ef Merge branch 'edit'|\| * df6ab19 Edit report* | 782d90b Remove report|/* 3fccb08 Add filesآزمونک
Section titled “آزمونک”در علامتهای conflict، بخش بین «<<<<<<< HEAD» و «=======» مال کدام طرف است؟
بالای ======= نسخهی شاخهی فعلی (HEAD) است و زیر آن تا >>>>>>> نسخهی شاخهی ادغامشده. برای دیدن نسخهی base از merge.conflictStyle=zdiff3 استفاده کن.
Git چه زمانی conflict میدهد؟
تغییرهای در جاهای مختلف فایل خودکار ادغام میشوند.
بعد از ویرایش فایل و پاککردن علامتها، چه باید کرد؟
add به Git میگوید فایل حل شد؛ بعد commit ادغام را تمام میکند.
git merge --abort چه میکند؟
بیخطر و بدون ردپا؛ فقط قبل از commit ادغام کار میکند.
در ادغام، «ours» و «theirs» چیست؟
git checkout --ours فایل را از شاخهی فعلی میگیرد.
علامتهای conflict را جا گذاشتهای و add و commit زدهای. چطور قبل از این اتفاق میگیریاش؟
و پروژه را بعد از حل اجرا و تست کن.
جمعبندی
Section titled “جمعبندی”- conflict یعنی دو شاخه یک بخش از یک فایل را متفاوت عوض کردهاند؛ Git بقیه را خودکار ادغام میکند و فقط همین بخش را به تو میسپارد.
- فایل علامت دارد:
<<<<<<< HEAD(شاخهی فعلی)=======(جداکننده)>>>>>>> نام(شاخهی ادغامشده). باmerge.conflictStyle=zdiff3نسخهی base هم میآید. - حل: فایل را درست کن (هیچ علامتی نماند) ←
git add فایل←git commit(یاgit merge --continue). - هر لحظه میتوانی با
git merge --abortبرگردی. یک طرف را کامل برداشتن:git checkout --ours/--theirs فایل(در ادغام: ours = شاخهی فعلی). - conflict ها فقط «دو نفر یک خط» نیستند:
UU،AA،DU،UD(حذف و ویرایش). git diff --checkعلامتهای جامانده را میگیرد؛ بعد از حل، تست کن.rerereحلهای تکراری را بهخاطر میسپارد.- برای کمکردن conflict: ادغامهای کوچک و مکرر، شاخههای کوتاهعمر، و هماهنگی با همکار.
| دستور | کاری که میکند |
|---|---|
git merge شاخه | شروع ادغام |
git status | فایلهای unmerged را ببین |
git diff | diff ترکیبی conflict |
(ویرایش فایل و حذف علامتها) | نسخهی نهایی |
git add فایل | علامت: حل شد |
git commit / git merge --continue | پایان ادغام |
git merge --abort | لغو ادغام |
git checkout --ours فایل / --theirs | نسخهی یک طرف |
git config merge.conflictStyle zdiff3 | نمایش base |
git diff --check | علامتهای جامانده |
git config rerere.enabled true | یادآور حلها |