فهرست مطالب
جواب را همین اول بدهم: روی شاخهای که هنوز push نکردهاید یا کسی دیگر رویش کار نمیکند، rebase بزنید تا تاریخچه صاف بماند؛ روی شاخهای که با بقیه مشترک است، فقط merge بزنید. تفاوتشان در همین یک چیز است: rebase کامیتهای شما را دوباره میسازد؛ merge هیچ کامیتی را دست نمیزند.
یک تاریخچهی واقعی
دو شاخه از یک نقطه جدا شدهاند: main یک کامیت مستندات دارد، feature دو کامیت کد دارد.
bash$ git log --oneline --graph --all
* 98962d6 بهروزرسانی مستندات
| * 6789051 افزودن logout
| * 712f76d افزودن login
|/
* 467df03 کامیت پایه
با merge هر دو تاریخچه کنار هم میمانند
bash$ git checkout feature
$ git merge main
$ git log --oneline --graph --all
* 221d4b7 Merge branch 'main' into feature
|\
| * 98962d6 بهروزرسانی مستندات
* | 6789051 افزودن logout
* | 712f76d افزودن login
|/
* 467df03 کامیت پایه
یک کامیت جدید (221d4b7) با دو والد ساخته شد. کامیتهای login و logout دقیقاً همان هش قبلی را دارند؛ هیچچیزی بازنویسی نشده و فقط یک نقطهی اتصال اضافه شده.
با rebase تاریخچه صاف میشود ولی کامیتها عوض میشوند
bash$ git checkout feature
$ git rebase main
$ git log --oneline --graph --all
* f8755f5 افزودن logout
* db44f8e افزودن login
* 98962d6 بهروزرسانی مستندات
* 467df03 کامیت پایه
نتیجه یک خط راست میشود؛ انگار از اول روی main کار کرده بودید. ولی نگاه کنید به هشها: login از 712f76d شد db44f8e، logout از 6789051 شد f8755f5. اینها کامیتهای جدیداند با همان محتوا؛ کامیتهای قبلی دیگر روی این شاخه نیستند. کامیتهای خود main هم دستنخورده ماندهاند؛ هش 98962d6 قبل و بعد از rebase یکی است.
چرا همین تفاوت، قانونِ «rebase نزن روی شاخهی pushشده» را میسازد
چون هش عوض شده، اگر همین شاخه را قبلاً push کرده باشید، push دوبارهی معمولی رد میشود:
bash$ git push origin feature
To ../project.git
! [rejected] feature -> feature (non-fast-forward)
error: failed to push some refs to '../project.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. If you want to integrate the remote changes,
hint: use 'git pull' before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
گیت شاخهی remote را جلوتر از شاخهی شما میبیند، چون کامیتهای قدیمی هنوز آنجا هستند و شما دارید یک تاریخچهی متفاوت با همان اسم میفرستید. hint پیشنهاد میدهد pull کنید، ولی اینجا pull کردن تاریخچهی قدیمی را دوباره وارد ماجرا میکند. تنها راه، push --force (یا امنترش، --force-with-lease) است؛ اگر همکارتان همان کامیتهای قدیمی را pull کرده باشد، کارش روی تاریخچهای میماند که دیگر وجود ندارد.
قانون عملی
| وضعیت | انتخاب |
|---|---|
| شاخهی شخصی، هنوز push نشده | rebase؛ تاریخچه تمیزتر میماند |
| شاخهای که دیگران هم رویش کار میکنند | فقط merge |
| میخواهید commit-by-commit مرور کنید | rebase قبل از باز کردن PR |
| مطمئن نیستید کسی دیگر pull کرده یا نه | merge؛ همیشه امن است |
اگر مطمئن نیستید، merge بزنید. یک کامیت merge اضافه در تاریخچه، خیلی کمتر از یک تاریخچهی بازنویسیشده روی شاخهی مشترک هزینه دارد.
اگر میخواهید کار تیمی روی گیتهاب را با پروژهی واقعی تمرین کنید، دورهی گیت و گیتهاب همین سناریوها را از شاخه و pull request تا حل تعارض، چند بار زیر دستتان میبرد. معرفیِ کاملتر تعارضها هم در پستِ چطور تعارض در Merge گیت را حل کنیم آمده است.





نظرها
اولین نفری باشید که نظر میدهدسؤالتان را بپرسید یا تجربهتان را بنویسید تا نویسندهی مطلب پاسخ بدهد.