Chapters ▾ 2nd Edition

2.4 مقدمات گیت (git basics chapter) - بازگرداندن تغییرات (Undoing Things)

بازگرداندن تغییرات (Undoing Things)

در هر مرحله ممکن است بخواهید کاری را بازگردانید. در اینجا، چند ابزار پایه برای بازگرداندن تغییراتی که ایجاد کرده‌اید مرور می‌کنیم. مواظب باشید، چون همیشه نمی‌توانید همه این بازگردانی‌ها را لغو کنید. این یکی از معدود موارد در گیت است که اگر اشتباه انجام شود، ممکن است بخشی از کارتان را از دست بدهید.

یکی از بازگردانی‌های رایج زمانی است که خیلی زود کامیت می‌کنید و احتمالاً برخی فایل‌ها را اضافه نکرده‌اید یا پیام کامیت خود را اشتباه نوشته‌اید. اگر می‌خواهید آن کامیت را اصلاح کنید، تغییرات اضافی که فراموش کرده‌اید را اعمال کنید، آنها را مرحله‌بندی (stage) کرده و دوباره با گزینه --amend کامیت کنید:

$ git commit --amend

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

ویرایشگر پیام کامیت همانند قبل باز می‌شود، اما پیام کامیت قبلی شما را در خود دارد. می‌توانید پیام را همانند همیشه ویرایش کنید، اما این پیام جایگزین کامیت قبلی خواهد شد.

مثلاً اگر کامیت کردید و بعد متوجه شدید تغییرات یک فایل که می‌خواستید به این کامیت اضافه کنید را آماده نکرده‌اید، می‌توانید کاری مشابه این انجام دهید:

$ git commit -m 'Initial commit'
$ git add forgotten_file
$ git commit --amend

در نهایت شما یک کامیت واحد خواهید داشت — کامیت دوم جایگزین نتایج کامیت اول می‌شود.

یادداشت

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

یادداشت

ارزش واضح اصلاح کامیت‌ها این است که بتوانید بهبودهای کوچک را روی آخرین کامیت خود انجام دهید بدون اینکه تاریخچه مخزن شما با پیام‌های کامیتی مانند «اوه، فراموش کردم یک فایل اضافه کنم» یا «لعنتی، دارم اشتباه تایپی در کامیت آخر را اصلاح می‌کنم» شلوغ شود. فقط کامیت‌هایی را اصلاح کنید که هنوز محلی هستند و به جایی ارسال نشده‌اند. اصلاح کامیت‌هایی که قبلاً ارسال شده‌اند و سپس ارسال اجباری شاخه باعث مشکلاتی برای همکارانتان خواهد شد. برای اطلاعات بیشتر در مورد اتفاقاتی که در این شرایط می‌افتد و چگونگی بازیابی در صورتی که در موقعیت دریافت‌کننده باشید، مطلب خطرات بازپایه‌گذاری (The Perils of Rebasing) را مطالعه کنید.

لغو آماده‌سازی یک فایل آماده‌شده (Unstaging a Staged File)

دو بخش بعدی نحوه کار با ناحیه مرحله‌بندی و تغییرات دایرکتوری کاری شما را نشان می‌دهد. نکته خوب این است که دستوری که برای تعیین وضعیت این دو ناحیه استفاده می‌کنید، همچنین به شما یادآوری می‌کند چگونه تغییرات آن‌ها را لغو کنید. برای مثال، فرض کنید دو فایل را تغییر داده‌اید و می‌خواهید آن‌ها را به‌صورت دو تغییر جداگانه کامیت کنید، اما به اشتباه دستور git add * را زده و هر دو را مرحله‌بندی کرده‌اید. چطور می‌توانید یکی از آن دو را از مرحله‌بندی خارج کنید؟ دستور git status به شما یادآوری می‌کند:

$ git add *
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    renamed:    README.md -> README
    modified:   CONTRIBUTING.md

در پایین متن «Changes to be committed» نوشته شده که از دستور git reset HEAD <file>…​ برای لغو مرحله‌بندی استفاده کنید. پس بیایید از این راهنمایی استفاده کنیم و فایل CONTRIBUTING.md را از مرحله‌بندی خارج کنیم:

$ git reset HEAD CONTRIBUTING.md
Unstaged changes after reset:
M	CONTRIBUTING.md
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    renamed:    README.md -> README

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   CONTRIBUTING.md

دستور کمی عجیب است، اما کار می‌کند. فایل CONTRIBUTING.md تغییر یافته اما دوباره از حالت استیج خارج شده است.

یادداشت

درست است که دستور git reset می‌تواند خطرناک باشد، به‌ویژه اگر گزینه --hard را بدهید. با این حال، در سناریوی بالا، فایل در دایرکتوری کاری شما دست نخورده باقی می‌ماند، بنابراین نسبتا امن است.

فعلا همین فراخوانی جادویی تمام چیزی است که باید درباره دستور git reset بدانید. در بازنشانی به زبان ساده (Reset Demystified) جزئیات بیشتری درباره عملکرد reset و چگونگی تسلط بر آن برای انجام کارهای جالب خواهیم گفت.

برگرداندن فایل تغییر یافته (Unmodifying a Modified File)

اگر متوجه شدید که نمی‌خواهید تغییرات فایل CONTRIBUTING.md را نگه دارید، چه می‌کنید؟ چطور می‌توانید به سادگی آن را به حالتی برگردانید که در آخرین کامیت (یا در ابتدا هنگام کلون کردن، یا هرطور که وارد دایرکتوری کاری‌تان شده) بود؟ خوشبختانه، git status به شما می‌گوید چطور این کار را انجام دهید. در خروجی مثال قبلی، بخش unstaged این‌گونه بود:

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   CONTRIBUTING.md

این به طور واضح به شما می‌گوید چطور تغییراتی که داده‌اید را دور بریزید. بیایید همان کاری را که می‌گوید انجام دهیم:

$ git checkout -- CONTRIBUTING.md
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    renamed:    README.md -> README

می‌بینید که تغییرات برگردانده شده‌اند.

مهم

مهم است بدانید که دستور git checkout — <file> یک دستور خطرناک است. هر تغییر محلی که در آن فایل داده‌اید از بین می‌رود — گیت فقط آن فایل را با آخرین نسخه استیج شده یا کامیت شده جایگزین می‌کند.

اگر می‌خواهید تغییرات‌تان را نگه دارید ولی فعلا می‌خواهید آن را کنار بگذارید، در انشعاب‌گیری در گیت (Git Branching) درباره stash و branch صحبت خواهیم کرد؛ این روش‌ها عموما بهتر هستند.

به یاد داشته باشید، هر چیزی که در گیت کامیت شده باشد تقریبا همیشه قابل بازیابی است. حتی کامیت‌هایی که روی شاخه‌هایی بودند که حذف شده‌اند یا کامیت‌هایی که با --amend بازنویسی شده‌اند هم قابل بازیابی هستند (برای بازیابی داده‌ها به بازیابی داده‌ها (Data Recovery) مراجعه کنید). اما هر چیزی که از دست بدهید و هرگز کامیت نشده باشد، احتمالا دیگر قابل بازیابی نیست.

بازگردانی تغییرات با git restore (Undoing things with git restore)

نسخه ۲.۲۳.۰ گیت یک دستور جدید معرفی کرد: git restore. این در واقع جایگزینی برای git reset است که همین حالا بررسی کردیم. از نسخه ۲.۲۳.۰ به بعد، گیت برای بسیاری از عملیات بازگردانی به جای git reset از git restore استفاده خواهد کرد.

بیایید دوباره مراحل را طی کنیم و با git restore به جای git reset عملیات بازگردانی را انجام دهیم.

لغو آماده‌سازی یک فایل با git restore (Unstaging a Staged File with git restore)

دو بخش بعدی نشان می‌دهند چگونه با استفاده از git restore با تغییرات در منطقه استیج و دایرکتوری کاری کار کنیم. نکته خوب این است که دستوری که برای مشاهده وضعیت این دو ناحیه استفاده می‌کنید، به شما یادآوری می‌کند چطور تغییراتشان را بازگردانید. مثلا فرض کنید دو فایل را تغییر داده‌اید و می‌خواهید به صورت دو تغییر جداگانه کامیت کنید، اما اشتباها git add * را زده و هر دو را استیج کرده‌اید. چطور یکی از آن دو را از حالت استیج خارج کنید؟ دستور git status به شما یادآوری می‌کند:

$ git add *
$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	modified:   CONTRIBUTING.md
	renamed:    README.md -> README

زیر عبارت “Changes to be committed” نوشته که برای خارج کردن فایل از استیج باید از git restore --staged <file>…​ استفاده کنید. پس بیایید طبق این راهنمایی فایل CONTRIBUTING.md را از استیج خارج کنیم:

$ git restore --staged CONTRIBUTING.md
$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	renamed:    README.md -> README

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   CONTRIBUTING.md

فایل CONTRIBUTING.md تغییر یافته اما دوباره unstaged شده است.

برگرداندن فایل تغییر یافته با git restore (Unmodifying a Modified File with git restore)

اگر متوجه شدید که نمی‌خواهید تغییرات فایل CONTRIBUTING.md را نگه دارید، چطور می‌توانید به سادگی آن را به حالتی برگردانید که در آخرین کامیت بود؟ خوشبختانه، git status به شما می‌گوید چطور این کار را انجام دهید. در خروجی مثال قبلی، بخش unstaged این‌گونه بود:

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   CONTRIBUTING.md

این به طور واضح به شما می‌گوید چطور تغییرات را دور بریزید. بیایید همان کاری را که می‌گوید انجام دهیم:

$ git restore CONTRIBUTING.md
$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	renamed:    README.md -> README
مهم

مهم است بدانید که دستور git restore <file> یک دستور خطرناک است. هر تغییر محلی که در آن فایل داده‌اید از بین می‌رود — گیت فقط آن فایل را با آخرین نسخه استیج شده یا کامیت شده جایگزین می‌کند. هرگز از این دستور استفاده نکنید مگر اینکه کاملا مطمئن باشید نمی‌خواهید آن تغییرات ذخیره نشده محلی را نگه دارید.

scroll-to-top