git — التراجع عن آخر commit مع الاحتفاظ بالتغييرات
ثلاثة أوامر تغطي تقريباً كل حالات التراجع: reset --soft/--mixed يحفظ العمل، و--hard مع reflog كشبكة أمان، وrevert لما دُفع بالفعل — وكل أمر يعمل كما هو.
remove أو delete أو cancel أو undo — أي فعل بحثت عنه، فإن الأمر الذي يحفظ التغييرات واحد. ثلاثة أوامر تغطي تقريباً كل
التراجع عن آخر commit مع الاحتفاظ بالتغييرات: git reset --soft HEAD~1 (مُدرَّجة staging) أو git reset HEAD~1 (غير مُدرَّجة).
دُفعت بالفعل؟ git revert HEAD — commit جديد يتراجع عنه، آمن على الفروع المشتركة.
reset --hard هو السطر الوحيد الذي يحذف عملاً — وgit reflog ما زال يعلم، لنحو 90 يوماً.
المسارات الثلاثة
commit زائد واحد، ثلاثة مخارج. العلم يقرر أين تهبط تغييراتك — اتبع المسارات:
الرسم — المسار الوحيد الذي يحذف عملاً هو --hard؛ وreflog هو المزرع الذي يجري تحت المسارات الثلاثة
الاحتفاظ بالتغييرات: الأمران
الحالة الأكثر شيوعاً: التزمت commit، ثم لاحظت خطأ مطبعياً أو ملفاً ناقصاً أو أدركت أن التغيير ينتمي إلى commit آخر. لم يُدفع شيء بعد. تراجع عن الـ commit وأعد كل شيء إلى مكانه:
# الـ commit رحل — التغييرات عادت إلى منطقة الـ staging
git reset --soft HEAD~1
# بدلاً من ذلك تعود التغييرات إلى شجرة العمل (غير مُدرَّجة)
git reset HEAD~1
HEAD~1 يعني "commit واحد قبل الموضع الذي يشير إليه HEAD الآن". بعد أيٍّ من الأمرين تبقى ملفاتك كما هي على القرص — لم يتحرك إلا مؤشر الفرع. تحقق عبر git status: with --soft تكون التغييرات مُدرَّجة، جاهزة لإعادة الالتزام مع دمج الإصلاحات؛ وبدون علم تكون غير مُدرَّجة، فتعديلها بحرية أولاً متاح.
تحقق: git status → مُدرَّجة (--soft) أو غير مُدرَّجة (افتراضي) · git log --oneline -1 → الـ commit السابق
عادة أكثر أماناً عندما تريد فقط إضافة ملفات إلى آخر commit ألا تتراجع عنه أصلاً:
git add forgotten-file.txt
git commit --amend --no-edit
--amend يستبدل آخر commit في مكانه (دون تغيير الرسالة هنا). انتبه: تعديل commit مَدْفوع يعيد كتابة التاريخ — المزيد أدناه.
الأعلام الثلاثة، جنباً إلى جنب
هذه الجزء الذي يستحق الحفظ، لأن العلم يقرر أين تنتهي تغييراتك — وهل يمكن أن تضيع:
| العلم | تراجع عن الـ commit؟ | التغييرات على القرص | مُدرَّجة؟ | الاستخدام النموذجي |
|---|---|---|---|---|
--soft | نعم | محفوظة | Yes | إعادة الالتزام مع إصلاحات صغيرة |
--mixed (الافتراضي) | نعم | محفوظة | No | إعادة تجميع التغييرات وإدراجها انتقائياً |
--hard | نعم | محذوفة | — | رمي العمل بأكمله |
git revert | لا (commit جديد) | محفوظة | — | التراجع عن commit دُفع بالفعل |
--hard هو الخطير الوحيد: فهو يتخلص من الـ commit و التغييرات معاً. قبل أي hard reset، خزّن عملك في stash أو فرع:
git branch backup-before-reset # تأمين رخيص
git reset --hard HEAD~1 # الـ commit + التغييرات رحلوا
شغّلت النسخة الخطرة بالفعل؟ ليس كل شيء ضائعاً — git reflog يتذكر أين مرّ HEAD. يحتفظ reflog بالـ commits المعلقة نحو 90 يوماً افتراضياً، فـ"أجريت hard-reset بالخطأ" شبه قابل للاستعادة إن تصرفت قبل garbage collection:
git reflog # أو: git reset --hard <hash>
git reset --hard HEAD@{1} # or: git reset --hard <hash>
تحقق: git reflog → الـ commit المُسقَط يقع في HEAD@{1}
دُفعت بالفعل؟
التاريخ المشترك يحصل على commit جديد، وليس إعادة كتابة أبداً. إذا كان الـ commit على فرع مشترك — أي شيء غير فرع الميزة الخاص بك — فلا تعيد كتابة التاريخ. git revert يحسب التغيير المعاكس ويلتزم به. كل من يجري pull يحصل ببساطة على commit جديد يزيل تغييرات القديم. لا force-push ولا زملاء انكسروا:
git revert HEAD
git push
Undoing a run of commits is the same move over a range: التراجع عن سلسلة من الـ commits هو الحركة نفسها على نطاق: . The alternative — — مقبول فقط على فرع لا يبني عليه أحد غيرك: — is only acceptable on a branch nobody else builds on: --force-with-lease يرفض الدفع إذا تحرك المستودع البعيد منذ آخر fetch؛ أما --force المجرد فيكتب فوق كل ما ظهر. الدفع القسري على الفروع المشتركة هو الطريقة التي تفقد بها الفرق الـ commits وتتوقف سجلات CI غموضاً عن مطابقة أي نسخة عمل.
تحقق: git log --oneline -2 → Revert "…" يجلس فوق الـ commit المتراجع عنه
عمل جيد، عنوان خاطئ
أحياناً الـ commit سليم — فرع خاطئ، أو مبكر جداً. بدل التراجع عنه، انقله. أوقف الـ commit كاملاً على فرع جديد، ثم نظّف
git branch stash-commit # أوقف الـ commit على فرع جديد
git reset --hard HEAD~1 # ثم نظّف فرعك الحالي
git cherry-pick <hash> # وأنت على الفرع الهدف
بين reset --soft, cherry-pick and revert لا يوجد commit لا يمكنك نقله أو إبطاله — الحيلة أن تختار "نقل" أو "إلغاء" قبل أن تمد يدك نحو علم.
الكلمة هي الأمر
نصف الالتباس مصطلحات: ستة أفعال يكتبها الناس في خانة البحث، وثلاثة أوامر موجودة فعلاً. طابق الكلمة في رأسك مع العلم الذي يقوم بها:
| الكلمة التي كتبتها | ما تريده | الأمر | هل حُفظت التغييرات؟ |
|---|---|---|---|
| undo / uncommit | الـ commit رحل، العمل عاد | git reset --soft HEAD~1 | نعم — مُدرَّجة |
| undo، غير مُدرَّجة | الـ commit رحل، العمل عاد فضفاضاً | git reset HEAD~1 | نعم — mixed |
| remove / drop | الـ commit والتعديلات رحلوا | git reset --hard HEAD~1 | لا — reflog نحو 90 يوماً |
| revert / cancel، مَدْفوع | إلغاء علني، التاريخ سليم | git revert HEAD | نعم — commit جديد |
الأفعال ليست مترادفة في Git: revert لا يلمس الماضي أبداً، بينما reset يعيد كتابته. قل الكلمة الخاطئة لزميل وستحصل على الأمر الخاطئ. القرار كله، كقائمة تحقق:
- لم يُدفع، تريد الإصلاح وإعادة الالتزام →
git reset --soft HEAD~1 - لم يُدفع، تريد الإدراج الانتقائي →
git reset HEAD~1(mixed) - تريد اختفاء التغييرات تماماً →
git reset --hard HEAD~1(reflog يعلم، إن ندمت) - دُفع بالفعل إلى فرع مشترك →
git revert HEAD - الـ commit ينتمي إلى فرع آخر →
cherry-pick، لا تتراجع عنه
تريد أن يقرر أحد عنك؟ يطرح مولّد git-undo هذه الأسئلة ويطبع الأمر. ونصيحة تشغيلية أخيرة: إذا وصل commit سيئ فعلاً إلى خادم — خطاف نشر خربش، أو عداء CI صار خارج السيطرة بعد force-push — فالمكان التالي للبحث هو سجلات الآلة لا Git: على أي آلة systemd،journalctl -u <service> -n 100 shows you exactly what ran and when. And when Git refuses to run at all — fatal: not a git repository — that is a different problem from undoing anything.
نهاية صفحة التراجع · لم يُجلب شيء · كل شيء محلي