از طریق منوی جستجو مطلب مورد نظر خود در وبلاگ را به سرعت پیدا کنید
کتاب راهنمای Git Rebase – راهنمای قطعی برای Rebasing
سرفصلهای مطلب
یکی از قدرتمندترین ابزارهایی که یک توسعه دهنده می تواند در جعبه ابزار خود داشته باشد این است git rebase
. با این حال، به دلیل پیچیده بودن و سوء تفاهم بدنام است.
حقیقت این است، اگر متوجه شوید که آن چیست در حقیقت میکند، git rebase
ابزاری بسیار زیبا و ساده برای دستیابی به موارد مختلف در Git است.
در پست های قبلی متوجه شدید که تفاوت های Git چیست، ادغام چیست و چگونه Git تداخل های ادغام را حل می کند. در این پست متوجه خواهید شد که Git rebase چیست، چرا با ادغام متفاوت است و چگونه با اطمینان دوباره بیایید.
نکاتی قبل از شروع
- من همچنین یک ویدیو ایجاد کردم که محتوای این پست را پوشش می دهد. اگر می خواهید در کنار خواندن تماشا کنید، می توانید آن را در اینجا بیابید.
- اگر می خواهید با مخزنی که من استفاده کردم بازی کنید و دستورات را برای خودتان امتحان کنید، می توانید مخزن را از اینجا دریافت کنید.
- من دارم روی کتابی درباره Git کار می کنم! آیا علاقه مند به خواندن نسخه های اولیه و ارائه بازخورد هستید؟ برای من یک ایمیل بفرستید: gitting.things@gmail.com
باشه، آماده ای؟
خلاصه کوتاه – Git Merge چیست؟ 🤔
در زیر کاپوت، git rebase
و git merge
چیزهای بسیار بسیار متفاوتی هستند پس چرا مردم همیشه آنها را با هم مقایسه می کنند؟
دلیل استفاده از آنهاست. هنگام کار با گیت معمولا در شاخه های مختلف کار می کنیم و تغییراتی را در آن شاخه ها معرفی می کنیم.
در یک آموزش قبلی، مثالی زدم که در آن جان و پل (از گروه بیتلز) آهنگ جدیدی را با هم می نویسند. آنها از main
شاخه، و سپس هر کدام از هم جدا شدند، اشعار را اصلاح کردند و تغییرات خود را انجام دادند.
سپس، این دو می خواستند تغییرات خود را ادغام کنند، چیزی که هنگام کار با Git اغلب اتفاق می افتد.
دو راه اصلی برای ادغام تغییرات معرفی شده در شاخه های مختلف در گیت یا به عبارت دیگر commit ها و commit history های مختلف وجود دارد. اینها ادغام و تغییر پایه هستند.
در آموزش قبلی با هم آشنا شدیم git merge
به خوبی. دیدیم که هنگام انجام یک ادغام، a ایجاد می کنیم ادغام commit – که محتویات این commit ترکیبی از دو شاخه است و همچنین دارای دو والد است که در هر شاخه یکی می باشد.
بنابراین، بگویید که در شعبه هستید john_branch
(با فرض تاریخچه نشان داده شده در نقاشی بالا)، و شما اجرا می کنید git merge paul_branch
. شما به این حالت خواهید رسید – کجا john_branch
، یک تعهد جدید با دو والدین وجود دارد. اولین مورد، commit on خواهد بود john_branch
شعبه کجا HEAD
قبل از انجام ادغام به آن اشاره می کرد، در این مورد – “Commit 6”. دوم تعهدی است که توسط آن اشاره شده است paul_branch
، “تعهد 9”.
دوباره به نمودار تاریخچه نگاه کنید: شما یک را ایجاد کردید واگرا شد تاریخ. در واقع می توانید ببینید که کجا شاخه شد و دوباره کجا ادغام شد.
بنابراین هنگام استفاده git merge
، تاریخ را بازنویسی نمی کنید – بلکه یک commit به تاریخچه موجود اضافه می کنید. و به طور خاص، تعهدی که تاریخچه ای واگرا ایجاد می کند.
چطور است git rebase
متفاوت از git merge
? 🤔
هنگام استفاده از git rebase
، اتفاق متفاوتی می افتد. 🥁
بیایید با تصویر بزرگ شروع کنیم: اگر در حال حاضر هستید paul_branch
، و استفاده کنید git rebase john_branch
، گیت به جد مشترک شاخه جان و شاخه پل می رود. سپس وصلههای معرفیشده در commitهای شاخه Paul را میگیرد و آن تغییرات را در شاخه John اعمال میکند.
بنابراین در اینجا، شما استفاده کنید rebase
برای برداشتن تغییراتی که در یک شاخه – شاخه پل – انجام شده و آنها را در شاخه دیگری پخش کنید john_branch
.
صبر کن یعنی چی؟ 🤔
اکنون این را ذره ذره بررسی می کنیم تا مطمئن شویم که به طور کامل متوجه می شوید که در زیر کاپوت چه اتفاقی می افتد.
cherry-pick
به عنوان پایه ای برای Rebase
این مفید است که rebase را به عنوان عملکردی در نظر بگیرید git cherry-pick
– یک فرمان یک commit می گیرد، آن را محاسبه می کند پچ این commit با محاسبه تفاوت بین commit والد و خود commit را معرفی می کند و سپس cherry-pick
این تفاوت را “بازپخش” می کند.
بیایید این کار را به صورت دستی انجام دهیم.
اگر به تفاوت معرفی شده توسط «Commit 5» با اجرا نگاه کنیم git diff main <SHA_OF_COMMIT_5>
:
(اگر می خواهید با مخزن مورد استفاده من بازی کنید و دستورات را برای خود امتحان کنید، می توانید مخزن را از اینجا دریافت کنید).
می بینید که در این کامیت، جان شروع به کار روی آهنگی به نام “Lucy in the Sky with Diamonds” کرد:
به عنوان یادآوری، می توانید از دستور نیز استفاده کنید git show
برای دریافت همان خروجی:
git show <SHA_OF_COMMIT_5>
حالا اگر شما cherry-pick
در این commit، این تغییر را به طور خاص در شاخه فعال معرفی خواهید کرد. تغییر به main
اولین:
git checkout main
(یا git switch main
)
و فقط برای روشن شدن یک شاخه دیگر ایجاد کنید:
git checkout -b my_branch
(یا git switch -c my_branch
)
و cherry-pick
این تعهد:
git cherry-pick <SHA_OF_COMMIT_5>
ورود به سیستم (خروجی از git lol
):
(git lol
نام مستعاری است که من به Git اضافه کردم تا تاریخچه را به صورت گرافیکی به وضوح ببینم. میتوانید اینجا پیدایش کنید).
انگار تو هستی کپی پیست شد “تعهد 5”. به یاد داشته باشید که حتی اگر پیام commit یکسانی داشته باشد، و تغییرات یکسانی را معرفی می کند، و حتی به همان شی درختی به عنوان “Commit 5” اصلی در این مورد اشاره می کند – همچنان یک شی commit متفاوت است، همانطور که با یک ایجاد شده است. مهر زمانی متفاوت
نگاهی به تغییرات، استفاده از git show HEAD
:
آنها همان “Commit 5” هستند.
و البته، اگر به فایل نگاه کنید (مثلاً با استفاده از nano lucy_in_the_sky_with_diamonds.md
)، در همان حالتی خواهد بود که پس از “Commit 5” اصلی بوده است.
سرد! 😎
خوب، اکنون می توانید شاخه جدید را حذف کنید تا هر بار در تاریخچه شما ظاهر نشود:
git checkout main
git branch -D my_branch
فراتر cherry-pick
– نحوه استفاده git rebase
می توانید نگاه کنید git rebase
به عنوان راهی برای انجام چندگانه cherry-pick
یکی پس از دیگری – یعنی “بازپخش” چندین commit. این تنها کاری نیست که می توانید با آن انجام دهید rebase
، اما نقطه شروع خوبی برای توضیح ما است.
زمان بازی است git rebase
! 👏🏻👏🏻
قبلا ادغام شدی paul_branch
به john_branch
. چه اتفاقی می افتد اگر شما مجدداً پایه گذاری شده است paul_branch
در بالای john_branch
? شما یک تاریخ بسیار متفاوت دریافت خواهید کرد.
در اصل، به نظر می رسد که ما تغییرات ارائه شده در commit ها را در نظر گرفته ایم paul_branch
، و آنها را دوباره پخش کرد john_branch
. نتیجه یک خواهد بود خطی تاریخ.
برای درک فرآیند، نمای سطح بالا را ارائه میدهم و سپس در هر مرحله عمیقتر فرو میروم. فرآیند تغییر پایه یک شاخه بر روی شاخه دیگر به شرح زیر است:
- جد مشترک را پیدا کنید.
- تعهداتی را که قرار است «بازپخش» شوند، شناسایی کنید.
- برای هر تعهد
X
، محاسبه کنیدdiff(parent(X), X)
، و آن را به عنوان یک ذخیره کنیدpatch(X)
. - حرکت
HEAD
به پایگاه جدید - پچ های تولید شده را به ترتیب روی شاخه هدف اعمال کنید. هر بار یک شی commit جدید با حالت جدید ایجاد کنید.
فرآیند ایجاد commit های جدید با همان تغییرات موجود نیز نامیده می شود “بازپخش” آن commit ها، اصطلاحی که قبلاً استفاده کرده ایم.
وقت آن است که با Rebase دست به کار شوید🙌🏻
از شعبه پل شروع کنید:
git checkout paul_branch
این تاریخ است:
و حالا به قسمت هیجان انگیزش می رسیم:
git rebase john_branch
و تاریخ را مشاهده کنید:
( gg
نام مستعار یک ابزار خارجی است که در ویدیو معرفی کردم).
بنابراین در حالی که با git merge
شما به تاریخ اضافه کردید، با git rebase
شما بازنویسی تاریخ. شما ایجاد می کنید جدید ارتکاب اشیاء علاوه بر این، نتیجه یک نمودار تاریخچه خطی است – نه یک نمودار واگرا.
در اصل، ما commit هایی را که در آن بودند «کپی» کردیم paul_branch
و بعد از “Commit 4” معرفی شد و آنها را در بالای آن “چسباند”. john_branch
.
این فرمان “rebase” نامیده می شود، زیرا commit پایه شاخه ای را که از آن اجرا می شود تغییر می دهد. یعنی در مورد شما قبل از اجرا git rebase
، پایه از paul_branch
“تعهد 4” بود – زیرا این شعبه “متولد” (از main
). با rebase
شما از گیت خواستید که پایگاه دیگری به آن بدهد – یعنی طوری وانمود کنید که گویی از «Commit 6» متولد شده است.
برای انجام این کار، Git آنچه قبلاً “Commit 7” بود را انتخاب کرد و تغییرات ایجاد شده در این commit را روی “Commit 6” “بازپخش” کرد و سپس یک شی commit جدید ایجاد کرد. این شی از سه جنبه با “Commit 7” اصلی متفاوت است:
- مهر زمانی متفاوتی دارد.
- این یک تعهد والدین متفاوت دارد – “Commit 6” به جای “Commit 4”.
- شی درختی که به آن اشاره می کند متفاوت است – زیرا تغییرات در درختی که توسط “Commit 6” به آن اشاره شده است، و نه درختی که توسط “Commit 4” به آن اشاره شده است، ارائه شده است.
به آخرین commit در اینجا، “Commit 9” توجه کنید. عکس فوری که نشان می دهد (یعنی درختی که به آن اشاره می کند) دقیقاً همان درختی است که با ادغام دو شاخه به دست می آورید. وضعیت فایل ها در مخزن Git شما خواهد بود همان مثل اینکه استفاده کردی git merge
. این فقط تاریخ است که متفاوت است، و البته موارد commit.
اکنون می توانید به سادگی استفاده کنید:
git checkout main
git merge paul_branch
هوم …. اگر این آخرین دستور را اجرا می کردید چه اتفاقی می افتاد؟ 🤔 پس از بررسی مجدد، تاریخچه ارتکاب را در نظر بگیرید main
:
ادغام به چه معناست main
و paul_branch
?
در واقع، Git می تواند به سادگی یک ادغام سریع به جلو انجام دهد، زیرا تاریخچه کاملاً خطی است (اگر به یادآوری در مورد ادغام سریع به جلو نیاز دارید، این پست را بررسی کنید). در نتیجه، main
و paul_branch
حالا به همان commit اشاره کنید:
Rebasing پیشرفته در Git💪🏻
اکنون که اصول rebase را درک کردید، زمان آن فرا رسیده است که موارد پیشرفته تری را در نظر بگیرید، جایی که سوئیچ ها و آرگومان های اضافی برای rebase
دستور به کار خواهد آمد.
در مثال قبل وقتی فقط گفتید rebase
(بدون سوئیچ های اضافی)، Git تمام commit ها را از جد مشترک تا نوک شاخه فعلی دوباره پخش کرد.
اما rebase یک قدرت فوق العاده است، این یک فرمان قادر مطلق است که می تواند … خوب، تاریخ را بازنویسی کند. و اگر میخواهید تاریخچه را برای خود تغییر دهید، میتواند مفید باشد.
آخرین ادغام را با ایجاد خنثی کنید main
دوباره به “Commit 4” اشاره کنید:
git reset -–hard <ORIGINAL_COMMIT 4>
و با استفاده از:
git checkout paul_branch
git reset -–hard <ORIGINAL_COMMIT 9>
توجه داشته باشید که دقیقاً به همان سابقه قبلی رسیده اید:
باز هم، برای روشن شدن، “Commit 9” فقط زمانی که از جریان فعلی قابل دسترسی نباشد ناپدید نمی شود. HEAD
. بلکه هنوز در پایگاه داده شی ذخیره می شود. و همانطور که شما استفاده کردید git reset
اکنون برای تغییر HEAD
برای اشاره به این commit، می توانید آن را بازیابی کنید، و همچنین والد آن نیز از آنجایی که آنها در پایگاه داده ذخیره می شوند، متعهد می شوند. خیلی باحاله، نه؟ 😎
خوب، به سرعت تغییراتی را که پل معرفی کرد مشاهده کنید:
git show HEAD
در نمودار commit به عقب ادامه دهید:
git show HEAD~
و یک ارتکاب بیشتر:
git show HEAD~2
بنابراین، این تغییرات خوب هستند، اما شاید پل این نوع تاریخ را نمی خواهد. بلکه می خواهد طوری به نظر برسد که تغییرات «Commit 7» و «Commit 8» را به صورت تک کامیت معرفی کرده است.
برای آن، می توانید از یک استفاده کنید در ارتباط بودن تغییر پایه برای انجام این کار، ما را اضافه می کنیم -i
(یا --interactive
) به rebase
دستور:
git rebase -i <SHA_OF_COMMIT_4>
یا، از آن زمان main
به “Commit 4” اشاره می کند، ما به سادگی می توانیم اجرا کنیم:
git rebase -i main
با اجرای این دستور، به Git میگویید که از یک پایه جدید، “Commit 4” استفاده کند. بنابراین شما از Git میخواهید که به تمام commitهایی که پس از «Commit 4» معرفی شدهاند و از زمان کنونی قابل دسترسی هستند بازگردد. HEAD
و آن تعهدات را دوباره پخش کنید.
برای هر commit که دوباره پخش می شود، Git از ما می پرسد که می خواهیم با آن چه کار کنیم:
در این زمینه مفید است که یک commit را به عنوان یک پچ در نظر بگیریم. یعنی «Commit 7» مانند «وصلهای که «Commit 7» در بالای والد خود معرفی کرد».
یکی از گزینه ها استفاده است pick
. این رفتار پیشفرض است که به Git میگوید تغییرات ایجاد شده در این commit را دوباره پخش کند. در این مورد، اگر آن را همانطور که هست رها کنید – و pick
همه commit ها – شما تاریخچه یکسانی را دریافت خواهید کرد و Git حتی اشیاء commit جدیدی ایجاد نمی کند.
گزینه دیگر این است squash
. آ له شده commit محتویات آن در محتویات commit قبل از آن “تا” می شود. بنابراین در مورد ما، پل مایل است “تعهد 8” را به “تعهد 7” تبدیل کند:
همانطور که می بینید، git rebase -i
گزینه های اضافی را ارائه می دهد، اما ما در این پست به همه آنها نمی پردازیم. اگر اجازه دهید rebase اجرا شود، از شما خواسته می شود که یک پیام commit را برای commit تازه ایجاد شده انتخاب کنید (یعنی پیامی که تغییرات “Commit 7” و “Commit 8” را ایجاد کرده است):
و به تاریخچه نگاه کنید:
دقیقا همانطور که ما می خواستیم! داریم paul_branch
“Commit 9” (البته، این یک شی متفاوت از “Commit 9” اصلی است). این به “Commits 7+8” اشاره می کند، که یک commit واحد است که تغییرات “Commit 7” و “Commit 8” اصلی را معرفی می کند. والد این commit “Commit 4” است، که در آن main
اشاره می کند به شما دارید john_branch
.
اوه وای، این باحال نیست؟ 😎
git rebase
به شما کنترل نامحدودی بر شکل هر شاخه می دهد. میتوانید از آن برای ترتیب دادن مجدد تعهدات یا حذف تغییرات نادرست یا اصلاح یک تغییر در نگاه به گذشته استفاده کنید. از طرف دیگر، می توانید پایه شاخه خود را به commit دیگری منتقل کنید، هر commitی که می خواهید.
نحوه استفاده از --onto
سوئیچ از git rebase
بیایید یک مثال دیگر را در نظر بگیریم. به main
از نو:
git checkout main
و نشانگرها را حذف کنید paul_branch
و john_branch
بنابراین دیگر آنها را در نمودار commit نمی بینید:
git branch -D paul_branch
git branch -D john_branch
و حالا منشعب از main
به شعبه جدید:
git checkout -b new_branch
اکنون، چند تغییر را در اینجا اضافه کنید و آنها را انجام دهید:
nano code.py
git add code.py
git commit -m "Commit 10"
برگشتن به main
:
git checkout main
و یک تغییر دیگر معرفی کنید:
زمان برای صحنه سازی و انجام این تغییرات:
git add code.py
git commit -m "Commit 11"
و یک تغییر دیگر:
این تغییر را نیز انجام دهید:
git add code.py
git commit -m "Commit 12"
اوه صبر کنید، اکنون متوجه شدم که می خواستم تغییراتی را که در “Commit 11” به عنوان بخشی از new_branch
. اوه چه کاری می توانی انجام بدهی؟ 🤔
تاریخ را در نظر بگیرید:
چیزی که من میخواهم این است که به جای اینکه “Commit 10” فقط روی آن باشد main
شاخه، من می خواهم آن را در هر دو باشد main
شعبه و همچنین new_branch
. از نظر بصری، میخواهم آن را در نمودار به پایین منتقل کنم:
میتونی ببینی کجا دارم میرم؟ 😇
خوب، همانطور که فهمیدیم، rebase اساساً به ما اجازه می دهد بازپخش تغییرات وارد شده در new_branch
، مواردی که در “ارتکاب 10” معرفی شده اند، به گونه ای که گویی در ابتدا به جای “ارتکاب 4” بر روی “ارتباط 11” انجام شده اند.
برای این کار می توانید از آرگومان های دیگر استفاده کنید git rebase
. به Git میگویید که میخواهید تمام تاریخچهای را که بین جد مشترک معرفی شده است، بگیرید main
و new_branch
، که “Commit 4” است و پایگاه جدید آن تاریخ “Commit 11” است. برای انجام این کار از:
git rebase -–onto <SHA_OF_COMMIT_11> main new_branch
و به تاریخ زیبای ما نگاه کنید! 😍
بیایید یک مورد دیگر را در نظر بگیریم.
بگو من در یک شعبه شروع به کار کردم و به اشتباه از آنجا شروع به کار کردم feature_branch_1
، به جای از main
.
بنابراین برای تقلید از این، ایجاد کنید feature_branch_1
:
git checkout main
git checkout -b feature_branch_1
و پاک کن new_branch
بنابراین دیگر آن را در نمودار نمی بینید:
git branch -D new_branch
یک فایل پایتون ساده به نام ایجاد کنید 1.py
:
این فایل را مرحله بندی و commit کنید:
git add 1.py
git commit -m "Commit 13"
اکنون منشعب شده (به اشتباه) از feature_branch_1
:
git checkout -b feature_branch_2
و یک فایل دیگر ایجاد کنید 2.py
:
این فایل را نیز استیج و commit کنید:
git add 2.py
git commit -m "Commit 14"
و چند کد دیگر را معرفی کنید 2.py
:
این تغییرات را نیز صحنه سازی و انجام دهید:
git add 2.py
git commit -m "Commit 15"
تا اینجا شما باید این سابقه را داشته باشید:
برگشتن به feature_branch_1
و ویرایش کنید 1.py
:
git checkout feature_branch_1
اکنون مرحله و متعهد شوید:
git add 1.py
git commit -m "Commit 16"
تاریخچه شما باید به این صورت باشد:
بگو حالا فهمیدی اشتباه کردی تو واقعا می خواستی feature_branch_2
به دنیا آمدن از main
شاخه، به جای از feature_branch_1
.
چگونه می توانید به آن دست پیدا کنید؟ 🤔
با توجه به نمودار تاریخچه و آنچه در مورد آن آموخته اید، سعی کنید در مورد آن فکر کنید --onto
پرچم برای rebase
فرمان
خوب، شما می خواهید “جایگزین” والد اولین commit خود شوید feature_branch_2
، که “متعهد 14” است، در بالای آن قرار گیرد main
شاخه، در این مورد، “متعهد 12″، به جای آغاز feature_branch_1
، در این مورد، “متعهد 13”. بنابراین دوباره، شما یک را ایجاد خواهید کرد پایگاه جدید، این بار برای اولین کامیت در feature_branch_2
.
چگونه می خواهید انجام دهید؟
ابتدا به feature_branch_2
:
git checkout feature_branch_2
و اکنون می توانید استفاده کنید:
git rebase -–onto main <SHA_OF_COMMIT_13>
در نتیجه شما دارید feature_branch_2
بر اساس main
به جای feature_branch_1
:
نحو از دستور زیر است:
git rebase --onto <new_parent> <old_parent>
چگونه بر روی یک شاخه واحد مجدداً پایه گذاری کنیم
همچنین می توانید استفاده کنید git rebase
در حالی که به تاریخچه یک شاخه نگاه می کنیم.
بیایید ببینیم می توانید در اینجا به من کمک کنید.
بگو من از آن کار کردم feature_branch_2
، و به طور خاص فایل را ویرایش کرد code.py
. من با تغییر دادن همه رشتهها به جای نقل قولهای تکی، با دو کوتیوم پیچیده شروع کردم:
سپس به صحنه بردم و متعهد شدم:
git add code.py
git commit -m "Commit 17"
سپس تصمیم گرفتم یک تابع جدید در ابتدای فایل اضافه کنم:
دوباره صحنه سازی کردم و متعهد شدم:
git add code.py
git commit -m "Commit 18"
و اکنون متوجه شدم که در واقع فراموش کرده ام که نقل قول های تکی را به نقل قول های دوتایی تغییر دهم __main__
(همانطور که ممکن است متوجه شده باشید)، بنابراین من این کار را نیز انجام دادم:
البته من این تغییر را صحنه سازی و انجام دادم:
git add code.py
git commit -m "Commit 19"
حال، تاریخچه را در نظر بگیرید:
واقعاً خوب نیست، اینطور است؟ منظورم این است که من دو commit دارم که به یکدیگر مرتبط هستند، “Commit 17” و “Commit 19” (چرخش '
به "
s)، اما آنها توسط “Commit 18” نامرتبط تقسیم می شوند (جایی که من یک تابع جدید اضافه کردم). چه می توانیم بکنیم؟ 🤔 میشه کمکم کنید؟
به طور مستقیم، من می خواهم تاریخچه را در اینجا ویرایش کنم:
بنابراین، چه کاری انجام می دهید؟
حق با شماست! 👏🏻
من می توانم تاریخچه را از “Commit 17” به “Commit 19” در بالای “Commit 15” تغییر دهم. برای انجام آن:
git rebase --interactive --onto <SHA_OF_COMMIT_15> <SHA_OF_COMMIT_15>
توجه داشته باشید که من “Commit 15” را به عنوان ابتدای محدوده commit ها، به استثنای این commit، مشخص کردم. و نیازی به ذکر صریح نداشتم HEAD
به عنوان آخرین پارامتر
پس از پیروی از توصیه شما و اجرای آن rebase
دستور (با تشکر! 😇) صفحه زیر را دریافت می کنم:
پس من چه کار کنم؟ من می خواهم “Commit 19” را بگذارم قبل از “Commit 18″، پس درست بعد از “Commit 17” می آید. می توانم جلوتر بروم و آنها را با هم له کنم، مانند این:
اکنون هنگامی که از من برای یک پیام commit خواسته می شود، می توانم پیام “Commit 17+19” را ارائه کنم:
و اکنون، تاریخ زیبای ما را ببینید:
بازم ممنون 🙌🏻
موارد استفاده Rebase بیشتر + تمرین بیشتر
در حال حاضر امیدوارم با نحو rebase احساس راحتی کنید. بهترین راه برای درک واقعی آن این است که موارد مختلف را در نظر بگیرید و نحوه حل آنها را خودتان بیابید.
با توجه به موارد استفاده آتی، اکیداً پیشنهاد می کنم پس از معرفی هر مورد استفاده، مطالعه را متوقف کنید و سپس سعی کنید آن را خودتان حل کنید.
نحوه حذف تعهدات
بگویید این سابقه را در مخزن دیگری دارید:
قبل از بازی کردن با آن، یک برچسب را در “Commit F” ذخیره کنید تا بتوانید بعداً به آن بازگردید:
git tag original_commit_f
اکنون، شما در واقع نمی خواهید تغییرات در “Commit C” و “Commit D” گنجانده شود. می توانید مانند قبل از یک rebase تعاملی استفاده کنید و تغییرات آنها را حذف کنید. یا، می تواند دوباره استفاده کند git rebase -–onto
. چگونه استفاده می کنید --onto
برای “حذف” این دو commit؟
شما می توانید rebase کنید HEAD
در بالای “Commit B”، جایی که والد قدیمی در واقع “Commit D” بود و اکنون باید “Commit B” باشد. تاریخ را دوباره در نظر بگیرید:
Rebasing به طوری که “Commit B” پایه “Commit E” باشد، به معنای “حرکت” هر دو “Commit E” و “Commit F” و دادن دیگری به آنها است. پایه – «تعهد ب». آیا می توانید خودتان دستور را ارائه دهید؟
git rebase --onto <SHA_OF_COMMIT_B> <SHA_OF_COMMIT_D> HEAD
توجه داشته باشید که استفاده از نحو بالا حرکت نمی کند main
برای اشاره به commit جدید، بنابراین نتیجه یک “جدایی” است HEAD
. اگر استفاده می کنید gg
یا ابزار دیگری که تاریخچه قابل دسترسی از شاخه ها را نشان می دهد ممکن است شما را گیج کند:
اما اگر به سادگی استفاده کنید git log
(یا نام مستعار من git lol
، تاریخچه مورد نظر را خواهید دید:
من در مورد شما نمی دانم، اما این نوع چیزها من را واقعا خوشحال می کند. 😊😇
به هر حال، شما می توانید حذف کنید HEAD
از دستور قبلی زیرا این مقدار پیش فرض برای پارامتر سوم است. بنابراین فقط با استفاده از:
git rebase --onto <SHA_OF_COMMIT_B> <SHA_OF_COMMIT_D>
همین تاثیر را خواهد داشت آخرین پارامتر در واقع به Git می گوید که پایان دنباله فعلی commits برای rebase کجاست. بنابراین نحو از git rebase --onto
با سه آرگومان است:
git rebase --onto <new_parent> <old_parent> <until>
نحوه جابجایی commit ها در شاخه ها
بنابراین بیایید بگوییم که به همان تاریخ قبلی می رسیم:
git checkout original_commit_f
و اکنون فقط “Commit E” را می خواهم که در یک شاخه مبتنی بر “Commit B” باشد. یعنی من می خواهم یک شعبه جدید داشته باشم که از «Commit B» منشعب می شود و فقط «Commit E» دارد.
بنابراین، این به چه معناست از نظر rebase؟ تصویر بالا را در نظر بگیرید. چه commit (یا commit هایی) را باید تغییر دهم و کدام commit پایه جدید خواهد بود؟
میدونم اینجا میتونم روی تو حساب کنم😉
آنچه من می خواهم این است که “Commit E” و فقط این commit را بگیرم و پایه آن را به “Commit B” تغییر دهم. به عبارت دیگر، به بازپخش تغییرات ارائه شده در “Commit E” بر روی “Commit B”.
آیا می توانید آن منطق را در نحو اعمال کنید git rebase
?
اینجاست (این بار دارم می نویسم <COMMIT_B>
بجای <SHA_OF_COMMIT_B>
برای اختصار):
git rebase –-onto <COMMIT_B> <COMMIT_D> <COMMIT_E>
اکنون تاریخ به این شکل است:
عالی!
نکته ای در مورد تعارضات
توجه داشته باشید که هنگام انجام یک rebase، ممکن است مانند هنگام ادغام با تداخل مواجه شوید. ممکن است تداخل داشته باشید زیرا هنگام تغییر پایه، سعی میکنید وصلهها را روی پایه دیگری اعمال کنید، شاید در جایی که وصلهها اعمال نمیشوند.
به عنوان مثال، مخزن قبلی را دوباره در نظر بگیرید، و به طور خاص، تغییر معرفی شده در “Commit 12” را که توسط main
:
git show main
من قبلاً به فرمت آن پرداختم git diff
به تفصیل در پست قبلی، اما به عنوان یک یادآوری سریع، این commit به Git دستور می دهد تا یک خط بعد از دو خط زمینه اضافه کند:
```
This is a sample file
و قبل از این سه خط زمینه:
```
def new_feature():
print('new feature')
بگویید که میخواهید «Commit 12» را به یک commit دیگر تغییر دهید. اگر به دلایلی، این خطوط زمینه مانند وصله مربوط به commit که در حال تغییر آن هستید وجود نداشته باشد. به سوی، در این صورت درگیری خواهید داشت. برای کسب اطلاعات بیشتر در مورد تضادها و نحوه حل آنها، به این راهنما مراجعه کنید.
بزرگنمایی برای تصویر بزرگ
در ابتدای این راهنما، من با ذکر شباهت بین این دو شروع کردم git merge
و git rebase
: هر دو برای ادغام تغییرات ارائه شده در تاریخچه های مختلف استفاده می شوند.
اما، همانطور که اکنون می دانید، آنها در نحوه عملکرد بسیار متفاوت هستند. در حالی که ادغام منجر به تاریخچه واگرا می شود، تغییر پایه منجر به یک تاریخچه خطی می شود. تعارض در هر دو مورد ممکن است. و یک ستون دیگر در جدول بالا توضیح داده شده است که نیاز به توجه دقیق دارد.
اکنون که می دانید “Git rebase” چیست و چگونه از rebase تعاملی یا استفاده کنید rebase --onto
همانطور که امیدوارم موافق باشید git rebase
یک ابزار فوق العاده قدرتمند است با این حال، در مقایسه با ادغام، یک اشکال بزرگ دارد.
Git rebase تاریخچه را تغییر می دهد.
این به این معنی است که شما باید نه rebase commit هایی که خارج از کپی محلی شما از مخزن وجود دارد و ممکن است افراد دیگر تعهدات خود را بر اساس آن بنا کرده باشند.
به عبارت دیگر، اگر تنها commit های مورد نظر آنهایی هستند که به صورت محلی ایجاد کرده اید – ادامه دهید، از rebase استفاده کنید، وحشی شوید.
اما اگر commit ها تحت فشار قرار گرفته باشند، این می تواند به یک مشکل بزرگ منجر شود – زیرا ممکن است شخص دیگری به این commit ها اعتماد کند، که بعداً آنها را بازنویسی می کنید، و سپس شما و آنها نسخه های متفاوتی از مخزن خواهید داشت.
این بر خلاف است merge
که همانطور که دیدیم تاریخ را تغییر نمی دهد.
به عنوان مثال، آخرین موردی را در نظر بگیرید که در آن این تاریخچه را مجدداً تغییر دادیم و به نتیجه رسیدیم:
حال، فرض کنید که من قبلاً این شاخه را به ریموت فشار داده ام. و بعد از اینکه شاخه را هل دادم، توسعه دهنده دیگری آن را کشید و از “Commit C” منشعب شد. توسعهدهنده دیگر نمیدانست که در همین حین، من به صورت محلی شعبهام را تغییر میدادم و بعداً دوباره آن را فشار میدادم.
این منجر به یک ناسازگاری می شود: توسعه دهنده دیگر از یک commit کار می کند که دیگر در نسخه مخزن من موجود نیست.
من در این راهنما در مورد اینکه دقیقاً چه چیزی باعث این می شود توضیح نمی دهم، زیرا پیام اصلی من این است که قطعاً باید از چنین مواردی اجتناب کنید. اگر به آنچه واقعاً اتفاق میافتد علاقه دارید، پیوندی به یک منبع مفید در زیر میگذارم. در حال حاضر، اجازه دهید آنچه را که پوشش داده ایم، خلاصه کنیم.
خلاصه
در این آموزش با این موضوع آشنا شدید git rebase
، ابزاری فوق العاده قدرتمند برای بازنویسی تاریخ در Git. شما چند مورد استفاده را در نظر گرفتید که در آن git rebase
می تواند مفید باشد، و نحوه استفاده از آن با یک، دو یا سه پارامتر، با و بدون آن --onto
تعویض.
امیدوارم تونسته باشم شما رو متقاعد کنم git rebase
قدرتمند است – اما این که پس از دریافت اصل مطلب بسیار ساده است. این ابزاری برای “کپی پیست” commit ها (یا دقیق تر، وصله ها) است. و این ابزار مفیدی است که باید در زیر کمربند خود داشته باشید.
مراجع اضافی
- لیست پخش Git Internals YouTube — توسط Brief (کانال YouTube من).
- پست قبلی Omer در مورد داخلی Git.
- آموزش Omer درباره Git UNDO – بازنویسی تاریخچه با Git.
- Git docs در rebasing
- انشعاب و قدرت rebase
- Rebasing تعاملی
- Git rebase –onto
درباره نویسنده
Omer Rosenbaum مدیر ارشد فناوری Swimm است. او نویسنده کانال کوتاه یوتیوب است. او همچنین کارشناس آموزش سایبری و بنیانگذار آکادمی امنیتی چک پوینت است. او نویسنده شبکه های کامپیوتری (به زبان عبری) است. می توانید او را در آن پیدا کنید توییتر.
منتشر شده در 1402-12-26 05:50:06