چگونه خطاهای رایج در گیت را رفع کنیم؟ (git reset vs revert vs rebase)
ﺯﻣﺎﻥ ﻣﻄﺎﻟﻌﻪ: 9 دقیقه

چگونه خطاهای رایج در گیت را رفع کنیم؟ (git reset vs revert vs rebase)

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

در چنین موقعیت‌هایی، دانستن ابزارهای عیب‌یابی گیت نه‌تنها ضروری، بلکه نجات‌دهنده است.
در این مطلب، با تمرکز بر این سه دستور کلیدی:

  • git reset
  • git revert
  • git rebase

به بررسی سناریوهای رایج و راه‌حل‌های ممکن می‌پردازیم. همچنین با مفاهیم مهمی مانند git log ،git reflog، حالت Detached HEAD و حل تعارض‌ها آشنا می‌شویم.

بازگرداندن تغییرات در گیت — کی و چگونه؟

گاهی اوقات هنگام کار با گیت، متوجه می‌شویم که تغییری را نباید اعمال می‌کردیم. شاید فایلی را اشتباهی ویرایش کرده‌ایم، یا حتی کامیتی انجام داده‌ایم که نباید ثبت می‌شد. در این مواقع، یکی از سوالات اصلی این است:

چگونه تغییرات را در گیت بازگردانیم؟

بازگرداندن تغییرات در گیت روش‌های مختلفی دارد، و انتخاب روش مناسب بستگی به این دارد که:

آیا تغییر فقط در فایل‌های محلی است؟

آیا تغییر کامیت شده؟

آیا کامیت به ریپازیتوری ریموت هم پوش داده شده؟

آیا می‌خواهیم تاریخچه را پاک کنیم یا فقط آن را اصلاح کنیم؟

۱. فایل‌ها را به آخرین وضعیت برگردانیم (قبل از کامیت)

اگر تغییری در فایلی داده‌اید ولی هنوز کامیت نکرده‌اید، می‌توانید با دستور زیر فایل را به آخرین نسخه‌ی کامیت‌شده بازگردانید:

git checkout -- filename

یا با گیت‌های جدیدتر:

git restore filename

هشدار: این دستور تغییرات شما را بدون بازگشت حذف می‌کند.

2. بازگردانی کل استیج (index)

اگر فایل‌ها را git add کرده‌اید اما هنوز commit نکرده‌اید، می‌توانید آن‌ها را از حالت استیج خارج کنید:

git reset

و اگر می‌خواهید هم از استیج خارج کنید و هم تغییرات را از فایل حذف کنید:

git reset --hard

استفاده از --hard باید با احتیاط انجام شود، چون تغییرات را به‌طور کامل از بین می‌برد.

۳. بازگرداندن یک کامیت

اگر تغییری را کامیت کرده‌اید ولی می‌خواهید آن را از تاریخچه حذف یا معکوس کنید، در ادامه خواهیم دید که git reset و git revert دو روش اصلی برای این کار هستند.

تفاوت reset و revert: دو راه برای پاک کردن اشتباهات

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

git reset

git revert

اما این دو دستور با وجود شباهت ظاهری، تفاوت‌های بنیادین دارند و استفاده‌ی نادرست از آن‌ها می‌تواند باعث آسیب جدی به تاریخچه پروژه شود—به‌ویژه اگر با مخزن اشتراکی (ریموت) کار می‌کنید.

git reset: بازنویسی تاریخچه

git reset تاریخچه گیت را به عقب برمی‌گرداند و آنچه بعد از نقطه‌ی انتخابی آمده را حذف می‌کند (بسته به نوع reset). این روش زمانی مناسب است که:

  • تغییرات فقط در مخزن محلی هستند.
  • هنوز هیچ چیز به ریموت پوش نشده.
  • می‌خواهید گیت را به حالت "قبل از اشتباه" برگردانید.

مثال:

git reset --hard HEAD~1

این دستور آخرین کامیت را پاک می‌کند و فایل‌ها را هم به حالت قبل از آن کامیت برمی‌گرداند.

حالت‌های مختلف reset:

  • --soft: فقط HEAD را جابجا می‌کند (کامیت حذف می‌شود اما تغییرات باقی می‌ماند).
  • --mixed: در این حالت HEAD جابجا می‌شود و تغییرات از استیج خارج می‌شوند.
  • --hard: همه‌چیز حذف می‌شود (کامیت + تغییرات فایل‌ها).

اگر تغییرات به ریموت پوش شده‌اند، استفاده از reset ممکن است باعث تعارض در برنچ‌های دیگر شود.

git revert: حفظ تاریخچه، ولی معکوس‌سازی تغییر
برخلاف reset، دستور git revert تاریخچه را تغییر نمی‌دهد. در عوض، یک کامیت جدید ایجاد می‌کند که اثرات یک یا چند کامیت قبلی را برعکس می‌کند.

مثال:

git revert HEAD

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

زمان مناسب برای استفاده از revert:

  • وقتی کامیت اشتباه قبلاً به ریموت push شده.
  • وقتی نمی‌خواهید تاریخچه پاک یا بازنویسی شود.
  • وقتی با تیمی کار می‌کنید و تاریخچه باید شفاف باقی بماند.

خلاصه تفاوت reset و revert

ویژگی git reset git revert
پاک‌کردن تاریخچه بله (در صورت --hard) خیر
ایمن برای ریموت خیر (اگر قبلاً پوش شده باشد) بله
سبک کار حذف یا بازنویسی تغییر افزودن کامیت معکوس
استفاده‌ی رایج اصلاح محلی، قبل از پوش اصلاح عمومی، بعد از پوش

git rebase: دوست قدرتمند و خطرناک شما

دستور git rebase یکی از پیشرفته‌ترین ابزارهای گیت برای بازنویسی تاریخچه است. اگرچه استفاده‌ی نادرست از آن می‌تواند دردسرساز شود، ولی وقتی به‌درستی استفاده شود، می‌تواند تاریخچه‌ای تمیز، منظم و خوانا تولید کند.

rebase چیست؟

rebase یعنی "پایه‌ی" یک برنچ را تغییر دادن. به‌جای اینکه یک برنچ را به‌صورت شاخه‌ای از commitهای جدید نگه دارید، گیت تغییرات شما را برداشته و آن‌ها را روی پایه‌ی جدیدی دوباره اجرا می‌کند.

مثال ساده:

git rebase main

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

کاربردهای رایج rebase

۱. همگام‌سازی شاخه‌ها با تاریخچه‌ای تمیز: به جای merge که یک کامیت جدید ایجاد می‌کند، می‌توانید با rebase، تاریخچه را صاف و بدون انشعاب نگه دارید.

۲. اصلاح کامیت‌ها با rebase تعاملی (interactive): یکی از کاربردهای قدرتمند rebase زمانی است که بخواهید کامیت‌های گذشته را:

  • ادغام (squash) کنید
  • حذف کنید
  • پیام آن‌ها را تغییر دهید
  • ترتیب آن‌ها را عوض کنید

مثال:

git rebase -i HEAD~3

در این حالت یک لیست از آخرین ۳ کامیت باز می‌شود و می‌توانید با دستوراتی مانند pick ،reword ،squash و ... آن‌ها را اصلاح کنید.

هشدارهای مهم درباره rebase

اگر تاریخچه‌ای که می‌خواهید rebase کنید قبلاً به ریموت push شده، اجرای rebase و سپس push با --force می‌تواند برای همکارانتان دردسر درست کند. همچنین در rebase ممکن است با تعارض مواجه شوید
اگر دو شاخه روی یک فایل تغییرات متضاد داده باشند، در حین rebase باید تعارض (conflict) را حل کنید.

git log و git reflog: نجات‌دهندگان نامرئی تاریخچه

گاهی در گیت کاری می‌کنید و ناگهان همه‌چیز به‌هم می‌ریزد. مثلا یک ری‌بیس اشتباه، ریست با --hard، یا حتی حذف کامیت‌های مهم. در این مواقع، دو دستور نجات‌بخش هستند:

  • git log
  • git reflog

بیایید ببینیم هرکدام دقیقاً چه کاری انجام می‌دهند و چه زمانی به کمک ما می‌آیند.

git log: تاریخچه‌ی رسمی کامیت‌ها

این دستور لیستی از کامیت‌های موجود در برنچ فعلی را نشان می‌دهد:

git log

خروجی معمولی شامل موارد زیر می‌شود:

  • هش کامیت (commit hash)
  • نام نویسنده
  • تاریخ
  • پیام کامیت

برای نسخه‌ی خلاصه‌تر نیز می‌توانید به شکل زیر عمل کنید:

git log --oneline --graph --all

این گزینه نمایش گرافیکی تاریخچه را ممکن می‌سازد و درک ساختار برنچ‌ها و ادغام‌ها را آسان می‌کند.

git reflog: تاریخچه‌ی مخفی حرکات HEAD

برخلاف log که فقط کامیت‌های موجود را نمایش می‌دهد، reflog تمامی حرکت‌های HEAD (و تغییرات برنچ‌ها) را ثبت می‌کند.

git reflog

این دستور می‌گوید HEAD شما در چه مراحلی به کدام کامیت‌ها اشاره کرده، حتی اگر آن کامیت‌ها دیگر در log معمولی نیایند.

مثلاً اگر با git reset --hard کامیتی را حذف کرده باشید، reflog می‌تواند کمک کند آن را بازیابی کنید:

git reset --hard HEAD@{2}

این دستور HEAD را به موقعیت دوم در لیست reflog برمی‌گرداند.

مثالی از یک کاربرد عملی

تصور کنید که به‌صورت اشتباه عملیات reset را انجام داده‌اید:

git reset --hard HEAD~3

حال همانطور که می‌دانید کامیت‌ها از دست رفته‌اند. اما با استفاده از دستور زیر:

git reflog

می‌توانید هش مربوط به موقعیت‌های قبلی را بدست آورید. در نهایت برای بدست آوردن تاریخچه کامل هر کامیت می‌توانید دستور زیر را وارد کنید:

git reset --hard <commit-hash>

آشنایی با حالت Detached HEAD در گیت

detached head

اگر تا به حال با پیامی مثل این مواجه شده‌اید:

You are in 'detached HEAD' state.

احتمالاً تجربه‌ی گیج‌کننده‌ای داشته‌اید. حالت Detached HEAD یکی از مفاهیم نه‌چندان واضح ولی بسیار مهم در گیت است که باید آن را به‌خوبی درک کرد. چون ممکن است تغییرات‌تان را در آن از دست بدهید!

HEAD چیست؟

در گیت، HEAD اشاره‌گر فعلی شما به آخرین کامیت در برنچی است که روی آن قرار دارید. مثلاً اگر روی برنچ main باشید، HEAD به آخرین کامیت main اشاره می‌کند. Detached HEAD در این حالت به وضعیتی اشاره می‌کند که شما دیگر روی هیچ برنچی نیستید، بلکه HEAD به یک کامیت مشخص اشاره دارد.

مثلاً اگر با دستور زیر به یک کامیت خاص بروید:

git checkout abc1234

گیت می‌گوید:

Note: checking out 'abc1234'.
You are in 'detached HEAD' state.

در این حالت:

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

چگونه از Detached HEAD خارج شویم و تغییرات را نجات دهیم؟

اگر در این حالت تغییری ایجاد کردید و می‌خواهید آن را حفظ کنید می‌توانید یکی از دو دستور زیر را وارد کنید:

git switch -c new-branch-name

یا

git checkout -b new-branch-name

این دستور یک برنچ جدید از آن موقعیت می‌سازد و تغییرات‌تان را نگه می‌دارد.

در پایان

در کار با گیت، اشتباه اجتناب‌ناپذیر است؛ چه یک کامیت اشتباه باشد، چه بازنویسی ناخواسته‌ی تاریخچه یا ورود به حالت Detached HEAD. اما قدرت گیت در این است که تقریباً هیچ‌چیز واقعاً از دست نمی‌رود—به شرط آنکه ابزارها و روش‌های عیب‌یابی را بشناسیم و درست از آن‌ها استفاده کنیم.

در این مطلب یاد گرفتیم:

  • چگونه تغییرات را پیش از کامیت یا پس از آن بازگردانیم.
  • تفاوت‌های مهم و کاربردی بین git reset و git revert.
  • چطور با git rebase تاریخچه‌ای تمیز و منظم ایجاد کنیم و کامیت‌ها را اصلاح کنیم.
  • چطور با git log و به‌ویژه git reflog، تاریخچه‌ی گم‌شده را بازیابی کنیم.
  • و در نهایت، معنای حالت Detached HEAD و راه نجات از آن را بررسی کردیم.

درک درست این مفاهیم نه‌تنها از آسیب به پروژه جلوگیری می‌کند، بلکه شما را به یک توسعه‌دهنده‌ی حرفه‌ای‌تر و مطمئن‌تر تبدیل می‌کند.

چه امتیازی برای این مقاله میدهید؟

خیلی بد
بد
متوسط
خوب
عالی
در انتظار ثبت رای

15 ساعت پیش
/@arastoo
ارسطو عباسی
کارشناس تولید و بهینه‌سازی محتوا

کارشناس ارشد تولید و بهینه‌سازی محتوا و تکنیکال رایتینگ - https://arastoo.net

دیدگاه و پرسش

برای ارسال دیدگاه لازم است وارد شده یا ثبت‌نام کنید ورود یا ثبت‌نام

در حال دریافت نظرات از سرور، لطفا منتظر بمانید

در حال دریافت نظرات از سرور، لطفا منتظر بمانید

ارسطو عباسی

کارشناس تولید و بهینه‌سازی محتوا

مقالات برگزیده

مقالات برگزیده را از این قسمت میتوانید ببینید

مشاهده همه مقالات