دليل

git checkout وswitch وrestore: متى تستخدم كلاً منها

آخر تحديث: · Skillaria.Top

يؤدي git checkout وظيفتين مختلفتين: تبديل الفروع واستعادة الملفات، لذلك وزّعت Git 2.23 مهامه بين git switch (للفروع) وgit restore (للملفات). للتبديل استخدم git switch الاسم، ولإلغاء تغييرات غير مُثبَّتة في ملف استخدم git restore الملف، واترك checkout للسكربتات القديمة والسيناريوهات المعتادة.

لماذا لـ git checkout وظيفتان

git checkout ينفّذ تاريخياً عمليتين مختلفتين جوهرياً. مع اسم فرع يبدّل HEAD ويحدّث مجلد العمل. ومع مسار ملف يأخذ نسخة الملف من الفهرس أو من commit ويكتب فوق تعديلاتك بصمت. أمر واحد ينتقل بين الفروع بأمان، ويتخلص في الوقت نفسه من عمل غير محفوظ دون رجعة.

لهذا ظهر في Git 2.23 أمران منفصلان: git switch للفروع، وgit restore للملفات. لم يُلغَ checkout، وهو يعمل بالكامل، لكن الكود الجديد والتوثيق وتلميحات Git نفسه تستخدم switch وrestore. إذا كنت تتعلم الآن فابدأ بهما، ومع ذلك يجب أن تعرف checkout، لأنه موجود في التعليمات والسكربتات وفي المقابلات.

مطابقة الأوامر

المهمةgit checkoutالأمر الجديد
الانتقال إلى فرعgit checkout maingit switch main
إنشاء فرع والانتقال إليهgit checkout -b featuregit switch -c feature
إنشاء فرع أو إعادة ضبطهgit checkout -B featuregit switch -C feature
العودة إلى الفرع السابقgit checkout -git switch -
الانتقال إلى commit (detached HEAD)git checkout 3f2a1c9git switch --detach 3f2a1c9
إلغاء التغييرات في ملفgit checkout -- app.jsgit restore app.js
إخراج ملف من الفهرسgit reset HEAD app.jsgit restore --staged app.js
أخذ ملف من فرع أو commit آخرgit checkout main -- app.jsgit restore --source=main --staged --worktree app.js
إلغاء كل التغييرات في مجلدgit checkout -- .git restore .

انتبه إلى -- في أوامر checkout. وهي فاصل يعني «ما بعده مسارات». وهي تحمي من الالتباس عندما يحمل الملف والفرع الاسم نفسه. في restore لا توجد هذه المشكلة، لأن الأمر يعمل على الملفات فقط.

git checkout للفروع

git checkout feature/login        # الانتقال
git checkout -b feature/login     # إنشاء من الـ commit الحالي والانتقال
git checkout -b hotfix origin/main   # إنشاء من فرع بعيد
git checkout -t origin/feature/x  # فرع محلي يتتبع الفرع البعيد

إذا لم يوجد فرع محلي لكن يوجد على الخادم فرع بهذا الاسم، فإن git checkout feature/x (وكذلك git switch feature/x) ينشئ الفرع المحلي ويضبط التتبع تلقائياً. وقبل ذلك يلزم git fetch، وإلا فلن يعرف Git بالفرع البعيد الجديد.

التبديل لا يُفقد التغييرات غير المُثبَّتة: تنتقل معك إلى الفرع الآخر إذا لم تتعارض معه. وعند التعارض يرفض Git ويقترح تثبيت التغييرات أو إخفاءها عبر git stash. الخيار -f (--force) يبدّل الفرع ويتخلص من التعديلات المحلية — وهذه عملية خطرة.

git switch: الفروع فقط

يكرر switch الجزء الخاص بالفروع في checkout، لكن بقواعد أصرم. الفرق الأساسي: لا ينتقل إلى commit أو وسم دون --detach صريح. الأمر git switch v1.0 سيعطي خطأً بدلاً من الانتقال الصامت إلى detached HEAD، لذلك يصعب أن تجد نفسك «خارج فرع» بالخطأ.

git switch main
git switch -c feature/cart
git switch -c feature/cart origin/main
git switch --detach v1.0
git switch -              # الفرع السابق
git switch -m main        # نقل التعديلات مع دمج ثلاثي

git restore: زر التراجع للملفات

يعيد restore الملفات إلى الحالة المحفوظة، ولا يمس تاريخ الـ commits إطلاقاً. ولكي تستخدمه بثقة يجب أن تتذكر مناطق Git الثلاث: مجلد العمل (ما تراه في المحرر)، والفهرس (ما أُعدّ عبر git add)، والـ commits.

كيف تلغي التغييرات في ملف قبل git add

git restore app.js         # إرجاع النسخة من الفهرس
git restore src/           # كل الملفات في المجلد
git restore .              # كل الملفات المتغيرة في المشروع

بدون خيارات يستعيد restore مجلد العمل من الفهرس. وإذا لم يُضَف الملف إلى الفهرس منذ آخر commit، فهذا يعني نفس نسخة آخر commit. تختفي التغييرات دون أثر ولا تذهب إلى سلة المهملات، لذلك انظر إلى git diff قبل git restore ..

كيف تُخرج ملفاً من الفهرس بعد git add

git restore --staged app.js

يتوقف الملف عن الاستعداد للـ commit، لكن تعديلاتك فيه تبقى. وهذه أكثر صور restore أماناً: لا يضيع شيء. وهذا الأمر نفسه هو ما يقترحه git status في قسم «Changes to be committed».

كيف تلغي كل شيء: في الفهرس وفي الملف

git restore --staged --worktree app.js
# الصيغة المختصرة
git restore -SW app.js

كيف تستعيد ملفاً من commit أو فرع آخر

git restore --source=HEAD~2 app.js          # نسخة قبل commitين
git restore --source=main config.yml        # نسخة من الفرع main
git restore --source=3f2a1c9 -- docs/       # مجلد كامل من commit

افتراضياً مع --source يتغير مجلد العمل فقط. أضف --staged إذا أردت إعداد التغيير للـ commit فوراً. أما الصيغة القديمة git checkout main -- app.js فتغيّر الفهرس ومجلد العمل معاً دائماً.

كيف تستعيد ملفاً محذوفاً

git restore deleted.txt                          # حُذف لكن لم يُثبَّت بعد
git restore --source=HEAD~1 deleted.txt          # الحذف مُثبَّت في commit

إذا لم تعرف في أي commit حُذف الملف، فابحث عنه: git log --oneline -- deleted.txt. آخر commit في القائمة هو الذي حُذف فيه الملف، والاستعادة تكون من أبيه: git restore --source=<hash>~1 deleted.txt.

restore وreset وrevert: لا تخلط بينها

الأوامر الثلاثة كلها «تلغي»، لكن على أعماق مختلفة:

  • restore — يعمل على الملفات في مجلد العمل والفهرس، ولا يغيّر التاريخ. آمن تماماً.
  • reset — يحرّك مؤشر الفرع إلى الخلف فيعيد كتابة التاريخ. --soft يُبقي التغييرات في الفهرس، و--mixed (الافتراضي) يُبقيها في مجلد العمل، و--hard يحذفها. خطر على الـ commits المرسلة في الفروع المشتركة.
  • revert — ينشئ commit جديداً عكسياً للـ commit المحدد. لا يُعاد كتابة التاريخ، لذلك هو الطريقة الصحيحة لإلغاء commit موجود بالفعل على الخادم.

قاعدة بسيطة: التغييرات لم تُثبَّت بعد — restore؛ الـ commit محلي فقط — reset؛ الـ commit صار عند الزملاء — revert.

Detached HEAD بعد checkout

بعد تنفيذ git checkout 3f2a1c9 أو git checkout v1.0 ستظهر لك رسالة طويلة «You are in 'detached HEAD' state». صار HEAD يشير إلى commit وليس إلى فرع. وهذا مفيد لرؤية نسخة قديمة أو لتشغيل الاختبارات عليها.

يمكن عمل commits في هذه الحالة، لكنها لن تنتمي إلى أي فرع. إذا غيّرت شيئاً وأردت حفظه:

git switch -c fix-for-v1

إذا كنت تنظر فقط فارجع: git switch - أو git switch main. وإذا خرجت دون أن تنشئ فرعاً، فالـ commits ما زالت متاحة عبر git reflog: ابحث عن الـ hash ونفّذ git branch rescue <hash>.

أخطاء شائعة

error: pathspec 'feature' did not match any file(s) known to git

لم يجد Git فرعاً ولا ملفاً بهذا الاسم. غالباً الفرع موجود على الخادم فقط ولم تُنفَّذ git fetch. نفّذها وأعد المحاولة. وتحقق من الكتابة: أسماء الفروع حساسة لحالة الأحرف.

تخلصت من التغييرات بالخطأ عبر checkout -- أو restore

التعديلات غير المُثبَّتة التي لم تكن في الفهرس لا يحفظها Git، ولا يمكن استعادتها بأدوات Git. يبقى سجل المحرر أو الـ IDE المحلي. أما إذا أُضيف الملف بـ git add فمحتواه موجود في قاعدة البيانات ككائن «معلَّق»، ويمكن العثور عليه عبر git fsck --lost-found.

ملف وفرع بالاسم نفسه

git checkout test عند وجود فرع test ومجلد test سيعطي تحذيراً بالالتباس. استخدم git switch test للفرع، وgit restore test (أو git checkout -- test) للملفات.

سيناريوهات يومية

مواقف تتكرر باستمرار، والأمر الصحيح لكل منها:

  • بدأت تعديل ملف ثم اتضح أنك سلكت طريقاً خاطئاً — git restore الملف.
  • أضفت ملفاً زائداً إلى الفهرس قبل الـ commit — git restore --staged الملف.
  • تحتاج فجأة إلى التحول لمهمة أخرى في منتصف العمل — git stash، ثم git switch الفرع؛ وعند العودة — git switch - وgit stash pop.
  • تريد أخذ ملف واحد من فرع مجاور دون دمج الفرع كله — git restore --source=feature/x --staged --worktree المسار.
  • تريد معرفة هل كان الخلل موجوداً في الإصدار القديم — git switch --detach v1.0، ثم الفحص، ثم git switch -.
  • تريد التراجع عن جزء من التغييرات في ملف — git restore -p الملف: يعرض Git كل مقطع ويسأل هل يُلغى.

الوضع التفاعلي -p (--patch) مفيد جداً: يوجد في checkout وrestore، ويتيح حذف مخرجات التصحيح مع الإبقاء على التعديلات المفيدة في الملف نفسه.

كيف تتحقق من النتيجة قبل التراجع

يتخلص restore وcheckout مع المسارات من التغييرات دون تأكيد، لذلك من المفيد أن ترى أولاً ما الذي سيضيع. يعرض git diff التعديلات في مجلد العمل مقارنة بالفهرس، وgit diff --staged ما هو مُعدّ للـ commit، وgit diff main -- app.js الفرق بين الملف ونسخته في فرع آخر. وإذا كنت مترداً فاحفظ الحالة الحالية بـ git stash: يمكن دائماً استرجاع التغييرات منه، بخلاف restore.

الخلاصة

git checkout يجمع بين تبديل الفروع واستعادة الملفات، وهذا بالضبط ما يجعله خطراً. للفروع استخدم git switch، وللملفات استخدم git restore: أحد الأمرين يغيّر موضع HEAD فقط، والآخر يغيّر محتوى الملفات فقط. الفروع وrestore وreset وrevert بأمثلتها الجيدة والسيئة مشروحة عملياً في دورة Git.

الأسئلة الشائعة

هل checkout قديم وغير مدعوم؟

لا، لم يُعلن عن إلغائه وسيبقى يعمل. لكن للاستخدام اليومي يُنصح بالأمرين الأضيق والأكثر أماناً switch وrestore.

من أي إصدار من Git يوجد switch وrestore؟

ظهر الأمران في Git 2.23 (عام 2019). ويمكن معرفة الإصدار بالأمر git --version.

هل يمكن التراجع عن git restore؟

إذا استُعيدت نسخة مجلد العمل فالتعديلات المتخلَّص منها لم يحفظها Git، ولا يمكن التراجع عنها. أما git restore --staged فآمن: تبقى التعديلات في الملف، ويكفي إضافتها من جديد بـ git add.

كيف أنتقل إلى فرع زميلي؟

نفّذ git fetch ثم git switch اسم-الفرع: ينشئ Git فرعاً محلياً مرتبطاً بـ origin/اسم-الفرع.

ما الفرق بين git checkout . وgit reset --hard؟

يعيد git checkout . (أو git restore .) الملفات المتغيرة إلى نسخة الفهرس دون المساس بالفهرس والتاريخ. أما git reset --hard فيعيد الفهرس ومجلد العمل معاً إلى الـ commit، وإذا أُعطي commit فإنه يحرّك الفرع أيضاً.

تعلّم هذا الموضوع بالفيديو

يُشرح هذا الموضوع بالتفصيل مع التطبيق العملي في دورة «Git من الصفر إلى مطور واثق».

الانتقال إلى الدورة