مقدمه

در درس قبل یاد گرفتیم که چگونه آخرین کامیت را با git commit --amend اصلاح کنیم. اما اگر اشتباهی که کرده‌ایم در یک کامیت قدیمی‌تر باشد چه؟ مثلاً دو یا سه کامیت قبل، یک غلط املایی در پیام داشته‌ایم یا فراموش کرده‌ایم یک تغییر کوچک را اعمال کنیم. در این شرایط، --amend دیگر به کار ما نمی‌آید.

برای ویرایش کامیت‌هایی که در عمق تاریخچه قرار دارند، باید از یکی از قدرتمندترین ابزارهای گیت استفاده کنیم: rebase تعاملی (interactive rebase). این ابزار به شما اجازه می‌دهد تا لیستی از کامیت‌ها را بازنویسی کنید، ترتیب آن‌ها را عوض کنید، آن‌ها را با هم ادغام کنید، یا پیام و محتوایشان را ویرایش کنید.

ویرایش کامیت‌های قدیمی با rebase تعاملی

دستور git rebase -i (که i مخفف interactive است) یک فرآیند چندمرحله‌ای را برای ویرایش تاریخچه آغاز می‌کند. این یک ابزار بسیار قدرتمند برای بازنویسی تاریخچه است و همان قانون طلایی در مورد آن صدق می‌کند: هرگز از آن برای بازنویسی تاریخچه عمومی که با دیگران به اشتراک گذاشته‌اید، استفاده نکنید.

فرآیند کلی کار به این صورت است:

  1. شروع rebase: شما باید به گیت بگویید که می‌خواهید کامیت‌ها را تا کجا به عقب برگردید و ویرایش کنید. معمولاً این کار با دستور git rebase -i HEAD~N انجام می‌شود که در آن N تعداد کامیت‌هایی است که می‌خواهید ویرایش کنید. برای مثال، HEAD~3 به شما اجازه ویرایش سه کامیت آخر را می‌دهد.
  2. ویرایش لیست اقدامات (TODO list): پس از اجرای دستور، گیت ویرایشگر متن شما را با لیستی از کامیت‌هایی که انتخاب کرده‌اید باز می‌کند. در ابتدای هر خط، کلمه pick نوشته شده که یعنی «این کامیت را بردار و نگه دار».
  3. انتخاب دستور: شما باید کلمه pick را برای کامیتی که قصد ویرایش آن را دارید، به edit (یا خلاصه آن e) تغییر دهید.
  4. اجرای ویرایش: پس از ذخیره و خروج از ویرایشگر، گیت تاریخچه را تا قبل از کامیت مشخص‌شده برمی‌گرداند و سپس در کامیتی که برای edit علامت زده بودید، متوقف می‌شود.
  5. اصلاح کامیت: در این حالت، شما می‌توانید هر تغییری که می‌خواهید اعمال کنید. فایل‌ها را ویرایش کنید، با git add آن‌ها را به stage اضافه کنید و در نهایت با git commit --amend محتوا یا پیام کامیت را اصلاح نمایید.
  6. ادامه rebase: پس از اینکه اصلاحات شما تمام شد، با دستور git rebase --continue به گیت می‌گویید که فرآیند را ادامه دهد و سایر کامیت‌ها را روی این کامیت اصلاح‌شده اعمال کند.

مثال ۱: تغییر پیام یک کامیت قدیمی

فرض کنید تاریخچه ما با git log --oneline به این صورت است و ما می‌خواهیم پیام کامیت دوم (با هش a1b2c3d) را اصلاح کنیم:

f4d5e6f (HEAD -> main) Add feature C
a1b2c3d Add featur B
1a2b3c4 Add feature A

چون این کامیت در ۲ کامیت آخر قرار دارد، ما rebase را روی ۳ کامیت آخر اجرا می‌کنیم تا به آن دسترسی داشته باشیم:

git rebase -i HEAD~3

ویرایشگر با متن زیر باز می‌شود (ترتیب کامیت‌ها از قدیمی به جدید است):

pick 1a2b3c4 Add feature A
pick a1b2c3d Add featur B
pick f4d5e6f Add feature C

# ... (دستورات راهنما) ...

برای تغییر پیام کامیت، می‌توانیم به جای edit از دستور ساده‌تر reword (یا r) استفاده کنیم. خط دوم را به شکل زیر تغییر می‌دهیم و فایل را ذخیره می‌کنیم:

pick 1a2b3c4 Add feature A
reword a1b2c3d Add featur B
pick f4d5e6f Add feature C

بلافاصله، گیت ویرایشگر را دوباره باز می‌کند، اما این بار فقط برای ویرایش پیام همان کامیت. ما غلط املایی را اصلاح می‌کنیم ("Add feature B")، فایل را ذخیره می‌کنیم و تمام! rebase با موفقیت به پایان می‌رسد.

مثال ۲: تغییر محتوای یک کامیت قدیمی

حالا فرض کنید در همان کامیت a1b2c3d فراموش کرده‌ایم یک فایل را اضافه کنیم. دوباره فرآیند را با git rebase -i HEAD~3 شروع می‌کنیم، اما این بار از دستور edit استفاده می‌کنیم:

pick 1a2b3c4 Add feature A
edit a1b2c3d Add featur B
pick f4d5e6f Add feature C

پس از ذخیره، گیت متوقف می‌شود و پیامی شبیه به این نمایش می‌دهد: `Stopped at a1b2c3d... Add featur B`. حالا ما در وضعیتی هستیم که می‌توانیم کامیت را اصلاح کنیم. فایل فراموش‌شده را ویرایش کرده یا می‌سازیم، سپس آن را add می‌کنیم:

git add forgotten-file.txt

سپس با --amend آن را به کامیت فعلی اضافه می‌کنیم:

git commit --amend --no-edit

(آپشن --no-edit از باز شدن ویرایشگر برای تغییر پیام جلوگیری می‌کند، چون ما فقط می‌خواستیم محتوا را تغییر دهیم).

در نهایت، به گیت می‌گوییم که فرآیند را ادامه دهد:

git rebase --continue

اکنون تاریخچه ما اصلاح شده و کامیت قدیمی شامل فایل جدید نیز هست. توجه کنید که با این کار، هش آن کامیت (و تمام کامیت‌های بعد از آن) تغییر خواهد کرد.