إذا كنت تستخدم Git لفترة من الوقت، فربما تكون قد صادفت سجلًا مليئًا بعمليات التثبيت غير المفيدة، ورسائل "قيد العمل"، والاختبارات السريعة، أو حتى عمليات التثبيت الفارغة التي تم إنشاؤها فقط لتشغيل خطاف أو مسار. في تلك اللحظة، تنظر إلى السجل وتفكر: "لا أحد يستطيع أن يفهم هذا"الخبر السار هو أنك لست وحدك: هذا يحدث لنا جميعًا، وهذا هو الغرض من الدعم. جيت rebase.
الأمر المثير للاهتمام هو أنه عند استخدامه بشكل صحيح، تتيح لك أداة git rebase "تنظيف" سجل الالتزاماتاجعلها منظمة، خالية من التشويش، وأسهل بكثير للمراجعة. يمكنك حذف عمليات التثبيت التجريبية، ودمج عدة عمليات في عملية واحدة، وإعادة ترتيبها، وتصحيح رسائل التثبيت، وحتى إزالة التغييرات من المستودع البعيد عبر عملية دفع إجبارية. دعونا نلقي نظرة، من خلال أمثلة عملية، على كيفية استخدام إعادة التأسيس لتحويل سجل التغييرات من فوضى عارمة إلى شيء منظم وسهل القراءة.
ما هو Git Rebase بالضبط ولماذا يؤثر على سجل التغييرات؟
عندما نتحدث عن التجاوز، فإننا نتحدث حرفياً عن تغيير نقطة بداية الفرعيأخذ Git التغييرات من فرعك ويعيد إنتاجها واحدة تلو الأخرى على قاعدة بيانات أخرى (عادةً الفرع رئيسي أو فرع بعيد مُحدَّث). الأمر كما لو أنك عدت بالزمن وبدأت عملك من نسخة مُختلفة، ولكنك احتفظت بالتغييرات.
تخيل أن مشروعك يحتوي على هذه القصة في الفرع الرئيسي: أ – ب – جتقوم بإنشاء فرع من الوظائف ثم تقوم بإجراء عمليتي تثبيت: د - هـفي هذه الأثناء، في الفرع الرئيسي، يقوم شخص ما بإضافة تعديل جديد. Fبدون إعادة التأسيس، ستبدو فروعك على النحو التالي:
A---B---C---F (main)
A---B---C---D---E (feature)
إذا قمت بدمج فرع الميزة الخاص بك في الفرع الرئيسي بالطريقة التقليدية، فستحصل على عملية دمج إضافية، والتي غالبًا ما تُنشئ تاريخ متشابك مع وجود عدة خطوط متقاطعة. أما مع إعادة التأسيس، من ناحية أخرى، فإن Git يأخذ التغييرات D و E ويعيد تطبيقها على F:
A---B---C---F---D'---E' (feature reescrita)
أولئك D' و E' هذه ليست نفس التغييرات تمامًا مثل التغييرات D و E: إنها "نسخ" بمعرفات جديدة (تجزئات). لهذا السبب نقول إعادة التأسيس. يعيد كتابة التاريخوالنتيجة هي سجل أكثر خطية، بدون عمليات دمج وسيطة تضيف تشويشًا فقط.

لماذا يجب أن يكون لديك سجل التزامات نظيف؟
إن السجل المنظم ليس مجرد مسألة جمالية. سجل نظيف يجعل العمل اليومي أسهل بكثير.يمكنك أن ترى بنظرة سريعة ما تم فعله ومتى ولماذا، دون الحاجة إلى المرور عبر عمليات دمج فارغة أو رسائل "إصلاح سريع" خالية من السياق.
عند دمج الفروع من خلال الدمج المستمر من الفرع الرئيسي، ينتهي بك الأمر بمجموعة من الالتزامات مثل "دمج الفرع 'main' في feature-x" التي لا يقدمون معلومات حقيقية حول تغييرات التعليمات البرمجيةوهذا يعقد مهامًا مثل:
- حدد موقع التغيير الذي تم فيه إدخال الخطأ باستخدام أدوات لمقارنة الملفاتلأن التاريخ مليء بعمليات الدمج غير ذات الصلة.
- مراجعة طلبات السحبلأنه يتعين عليك التنقل بين عمليات الالتزام المتكررة لمعرفة ما تغير بالفعل.
- فهم تطور سمة ماوخاصة إذا تم تطويره عبر العديد من عمليات الاختبار الصغيرة.
تُعد أداة Git rebase الأداة المثالية "لصقل" كل تلك الضوضاء قبل مشاركة الفرع مع بقية الفريق. الأمر أشبه بوضع تاريخك في غسالة الملابس.احتفظ بالتغييرات المهمة، مصنفة بشكل جيد وبرسائل واضحة.
الاختلافات الرئيسية بين git merge و git rebase
لفهم ما تفعله تمامًا عند استخدام إعادة التأسيس (rebase)، يجدر مقارنة سلوكها بسلوك... دمج gitكلاهما يساهم في دمج التغييرات، ولكن بطرق مختلفة وبآثار مهمة على السجل.
مع دمجيُنشئ Git عملية دمج جديدة لها أصلان: رأس فرعك ورأس الفرع الذي تدمج معه. والسجل الناتج يحافظ على التسلسل الأصلي لعمليات الالتزام بدقةلكن قد ينتهي الأمر بوجود العديد من الفروع المتوازية وعمليات الدمج.
مع rebaseبدلاً من إنشاء عملية دمج، يأخذ Git عمليات الالتزام الخاصة بك ويطبقها واحدة تلو الأخرى على قاعدة البيانات الجديدة. وهذا يُنشئ تحديثات جديدة مع تجزئات جديدةحتى لو كانت تغييرات الكود هي نفسها.
في التمرين:
- دمج إنها تحافظ على التاريخ كما حدث تماماً، بكل روابطه المتشابكة وعمليات دمجه.
- ريباسي إنه يُنشئ تاريخًا خطيًا ونظيفًا، كما لو أن كل شيء قد حدث في خط مستقيم.
الخيار ليس "أحدهما أفضل من الآخر" ولكن ما هي الحالة الأنسب لكل منهما؟لدمج الفروع المشتركة والمغلقة، يُعدّ الدمج عادةً الخيار الأكثر أمانًا. أما للحفاظ على تنظيم فروع العمل المحلية أو قبل فتح طلب سحب، فإن إعادة التأسيس (rebase) تُعدّ خيارًا ممتازًا.

الحالات التي يتألق فيها إعادة التأسيس: تحديث الفروع وصقلها
هناك سيناريوهان يستخدم فيهما معظم المطورين إعادة التأسيس بشكل يومي: حافظ على تحديث فرع العمل الخاص بك مع الفرع الرئيسي y قم بتنظيف التغييرات قبل مشاركتها.دعونا نلقي نظرة عليها بمزيد من التفصيل.
قم بتحديث فرع الميزة الخاص بك بأحدث التغييرات من الفرع الرئيسي
أنت تعمل على ميزة جديدة في فرعك، ولكن في هذه الأثناء لا يزال زملاؤك يقومون بإجراء عمليات التثبيت في رئيسيإذا كنت ترغب في دمج التغييرات التي أجريتها، فإحدى الخيارات هي:
git checkout tu-rama-feature
git merge main
هذا يعمل، ولكنه يُنشئ عملية دمج في كل مرة تقوم فيها بالمزامنة، مما يتسبب في النهاية في حدوث مشاكل. تاريخ مليء بعمليات الدمج المتكررةلكن إذا فعلت ذلك:
git checkout tu-rama-feature
git rebase main
سيقوم Git بنقل عمليات الالتزام الخاصة بك فوق آخر حالة للفرع الرئيسي، كما لو كنت قد بدأت الفرع بعد تلك التغييرات. ستحصل على تاريخ خطي، بدون عمليات دمج وسيطةوستكون المراجعة أكثر وضوحاً.
قم بتنظيف عمليات الالتزام قبل فتح طلب سحب.
من الشائع جدًا، أثناء تطوير ميزة ما، أن ينتهي بك الأمر بإجراء تعديلات مثل "إصلاح خطأ إملائي"، و"اختبار خط الأنابيب"، و"المزيد من تغييرات تسجيل الدخول"، وما إلى ذلك. لا بأس بذلك أثناء العمل، ولكن هذا ليس نوع التاريخ الذي تريد عرضه. عند فتح طلب سحب.
تتيح لك خاصية إعادة التأسيس التفاعلية أعد تنظيم وتجميع تلك الالتزامات إلى شيء أكثر منطقية. على سبيل المثال، بدلاً من هذا:
- WIP: añadir login
- Más cambios login
- Corregir tests login
- Arreglar typo variable
يمكنك أن تنتهي بعملية إيداع واحدة موصوفة بشكل جيد:
- Añadir funcionalidad completa de login de usuario con tests
إن "صقل" التاريخ هذا يجعل فهمه أسهل بكثير بالنسبة للمراجعين. ما تُساهم به كل عملية إيداع ويسهل ذلك عملية استكشاف الأخطاء وإصلاحها في المستقبل.
إعادة التأسيس التفاعلية لإعادة كتابة التاريخ
تقوم النسخة الأساسية من إعادة التأسيس ببساطة بنقل عمليات الالتزام إلى قاعدة أخرى. لكن جوهرة التاج هي قاعدة تفاعلية، والذي يفتح محررًا يحتوي على قائمة بالتغييرات الأخيرة حتى تتمكن من تحديد ما يجب فعله بكل منها.
الطريقة المعتادة للبدء هي تحديد عدد التغييرات التي تريد تعديلها من النسخة الحالية (HEAD). على سبيل المثال:
git rebase -i HEAD~6
يُخبر هذا الأمر Git بما يلي: "خذ آخر 6 عمليات تثبيت من هذا الفرع وجهّز عملية إعادة ترتيب تفاعلية لي." سيفتح Git محرر النصوص الافتراضي لديك (Vim، Nano، VS Code، إلخ) مع أمر مشابه لما يلي:
pick 7ed9c6e update version
pick ecb7ef3 empty commit 1
pick a323615 empty commit 2
pick 2c3d41d empty commit 3
pick d53c00f empty commit 4
pick 22dcc79 empty commit 5
# إعادة ترتيب 549dd76..22dcc79 على 549dd76 (6 أوامر)
#
# الأوامر:
ص، اختر استخدم الالتزام
ر، إعادة صياغة استخدم أمر commit، ولكن عدّل الرسالة.
رقم هـ، تحرير استخدم الأمر commit ثم stop لتعديله.
# s, squash = دمج الالتزام السابق
# f، إصلاح = مثل الاسكواش، ولكن مع تجاهل الرسالة
# x، تنفيذي = تنفيذ أمر shell
إلى ب، توقف = أوقف إعادة التأسيس هنا
و د، إسقاط = حذف الالتزام
#…
لاحظ تفصيلاً مهماً واحداً: الترتيب في هذا الملف هو عكس ما تراه في سجل gitفي الملف، يُعدّ أول تعديل مُدرج (7ed9c6e) أقدم التعديلات الستة، بينما يُعدّ آخرها (22dcc79) أحدثها. وهذا أمرٌ أساسي لتجنب الالتباس عند حذف التعديلات أو إعادة ترتيبها.
قم بإزالة عمليات التثبيت التجريبية أو الفارغة من السجل المحلي
لنفترض أنك، لاختبار خطاف Git، كنت تقوم بإجراء عمليات تثبيت فارغة باستخدام الخيار –السماح بالفراغ. على سبيل المثال:
git commit -m "commit with no changes" --allow-empty
يتيح لك هذا الخيار إنشاء عملية حفظ حتى في حالة عدم وجود تغييرات على الملفات، وهو أمر مفيد للاختبار، ولكن إنه يُشوش سجل التغييرات بعمليات لا قيمة حقيقية لها.تخيل أن سجلّك يبدو هكذا:
git log --pretty=oneline --abbrev-commit
وستحصل على:
22dcc79 (HEAD -> main, origin/main, origin/HEAD) empty commit 5
d53c00f empty commit 4
2c3d41d empty commit 3
a323615 empty commit 2
ecb7ef3 empty commit 1
7ed9c6e update version
في هذا السيناريو، تريد الاحتفاظ فقط بالتحديث المفيد "إصدار التحديث" وإزالة جميع التحديثات الأخرى. التزام فارغ والتي كانت مجرد اختبارات. وللقيام بذلك، عليك تشغيل عملية إعادة التأسيس التفاعلية على آخر 6 عمليات تثبيت:
git rebase -i HEAD~6
يفتح المحرر مع ستة تغييرات مُسجلة. هدفك هو حذف الأسطر التي تُقابل التغييرات الفارغة. أي، أن تُبقي فقط على شيء كهذا:
pick 7ed9c6e update version
# إعادة ترتيب 549dd76..22dcc79 على 549dd76 (6 أوامر)
# … باقي التعليقات …
كما هو موضح في قسم المساعدة الخاص بالملف نفسه، إذا قمت بحذف سطر، فسيختفي هذا الالتزام من السجل. في النسخة الجديدة المعاد كتابتها، عند حفظ وإغلاق المحرر، سيقوم Git فقط بإعادة إنتاج عملية الالتزام "تحديث الإصدار" وتجاهل العمليات الأخرى.
تحميل القصة الجديدة إلى Origin: باستخدام خاصية الدفع القسري
حتى الآن، تم تطبيق جميع تغييرات إعادة التأسيس على نسختك المحلية من المستودع. إذا كنت ترغب في أن ينعكس هذا التنظيف أيضًا في المستودع البعيد (على سبيل المثال، في الأصل/الرئيسي)، سيتعين عليك استبدال السجل البعيد بالسجل الجديد.
يتم ذلك باستخدام دفع قسريإحدى الطرق الشائعة للإشارة إلى ذلك هي استخدام علامة "+" أمام اسم الفرع عند الدفع:
git push origin +main
تشير علامة الزائد هذه إلى أن Git تجاهل اختلاف التاريخ واستبدل الفرع البعيد مع نسختك المُعاد كتابتها. إذا كنت قد حاولت ببساطة القيام بما يلي:
git push origin main
سيحذرك Git من أن سجلك المحلي ليس سليلًا مباشرًا للسجل البعيد (لأنك قمت بإعادة كتابة الالتزامات باستخدام rebase) وسيرفض عملية الدفع لتجنب فقدان البيانات.
من المهم أن نفهم أنه بعد هذه الدفعة القسرية، لن يكون بالإمكان الوصول إلى عمليات الالتزام المحذوفة من origin/mainقد لا تزال موجودة في سجلات المراجع أو النسخ المحلية من زملاء آخرين، ولكن لأغراض عملية، قمت بمسح السجل البعيد.
تحذير هام: إعادة كتابة الفروع المشتركة ليست لعبة.
إعادة كتابة التاريخ تبدو فكرة رائعة لتنظيم كل شيء، لكن لها نتيجة رئيسية: عند إنشاء التزامات جديدة برموز تجزئة جديدة، أنت بذلك تُربك أي شخص كان قد اعتمد عمله بالفعل على التعديلات القديمة.ولهذا السبب توجد القاعدة الذهبية الشهيرة:
لا تقم بإعادة توجيه الفروع التي تمت مشاركتها بالفعل ويستخدمها الآخرون بنشاط.
عمليًا، هذا يعني أنه من الآمن استخدام إعادة التأسيس على:
- فروع الميزات المحلية التي لم تنشرها بعد أو التي تستخدمها أنت فقط.
- الالتزامات الأخيرة أولئك الذين تعلم أنه لا أحد آخر يعتمد عليهم بعد.
وهي فكرة سيئة للغاية أن تفعل ذلك بشأن:
- الفرع الرئيسي أو التطويري أو أي فرع "رسمي" من المستودع الذي يشكل أساسًا للعديد من الأشخاص.
- فرع العمل المشترك حيث يوجد أكثر من مطور واحد يقوم بإجراء عمليات الإيداع.
إذا قمت بإعادة كتابة تاريخ فرع مشترك ثم قمت بعملية دفع قسري، فسيكتشف زملاؤك الآخرون ذلك. تشير فروعها إلى عمليات تثبيت لم تعد موجودة في البعيدسيتعين عليهم القيام بعمليات أكثر تقدماً (مثل إعادة التأسيس على السجل الجديد أو إعادة التعيين) لإصلاح الفوضى.
الدفع بالقوة: يكون أفضل مع حزام الأمان
في كثير من الحالات، ستحتاج بعد إعادة التأسيس إلى فرض عملية الدفع. الخيار الكلاسيكي هو:
git push --force origin tu-rama
مع ذلك، يقوم هذا الخيار باستبدال أي بيانات على الجهاز البعيد دون استئذان. ولتجنب الكتابة عن طريق الخطأ فوق أعمال الآخرين، يوفر Git خيارًا أفضل بكثير: – الإلزام بالتأجير.
عندما تفعل ذلك:
git push --force-with-lease origin tu-rama
يتحقق Git أولاً من ذلك لا يزال جهاز التحكم عن بعد على الحالة التي كنت تعتقد أنها عليها. عند قيامك بآخر عملية سحب أو جلب، إذا اكتشف النظام أن شخصًا ما قد دفع تغييرات جديدة إلى هذا الفرع منذ ذلك الحين، فسيتم رفض عملية الدفع ولن يتم فرضها، مما يمنعك من الكتابة فوق تغييرات الآخرين.
الفكرة هي الجمع بين إعادة التأسيس والدفع القسري، مع الحفاظ على هدوء الأعصاب دائمًا: فقط في الفروع التي تتحكم بها واستخدام –force-with-lease كلما أمكن ذلك كطبقة إضافية من الأمان.
ماذا تفعل عندما تفشل عملية إعادة التأسيس؟ استخدم سجل التغييرات (reflog) للإنقاذ.
لقد حدث هذا لنا جميعًا: تبدأ عملية إعادة ضبط تفاعلية، ثم تحذف أو تغير شيئًا ما عن طريق الخطأ، أو تحل تعارضًا بشكل غير صحيح، وفجأة يبدو أنك فقدت بعضًا من وظيفتكقبل أن تشعر بالذعر، تذكر أن لدى Git ورقة رابحة في جعبته: git reflog.
سجل المراجع هو سجل محلي لجميع تحركات HEAD: عمليات الالتزام الجديدة، وإعادة التعيين، وإعادة التأسيس، وعمليات الدمج... بفضله، يمكنك تحديد مكان فرعك قبل أن تفسده والعودة إلى تلك النقطة بإعادة تعيين كاملة.
سيكون التدفق النموذجي على النحو التالي:
git reflog
ستجد هناك قائمة بالمدخلات تحتوي على شيء مثل:
abc1234 HEAD@{0}: rebase terminado
def5678 HEAD@{1}: checkout: moving from main to main
...
تحدد الالتزام الذي كنت عليه قبل بدء إعادة التأسيس (على سبيل المثال، ديف 5678ثم تعود إليه بـ:
git reset --hard def5678
وهكذا، يمكنك إلغاء التراكب تمامًا وتعود إلى الحالة السابقة. يمكنك أيضًا إيقاف عملية إعادة التأسيس الجارية (إذا كنت في منتصف العملية ولم تنتهِ منها) ببساطة باستخدام:
git rebase --abort
هذا الأمر يترك فرعك كما كان قبل بدء عملية إعادة التأسيس الحالية، وهو مثالي عندما تظهر تعارضات لا تريدها أو عندما ترى أن الوقت ليس مناسبًا للمتابعة.
Git pull مقابل git pull --rebase: اختلافات طفيفة، تأثير كبير
وثمة نقطة أخرى تثير الشكوك في كثير من الأحيان، وهي الفرق بين سحب بوابة عادي و git pull --rebaseكلاهما يقوم بتحديث فرعك المحلي من الفرع البعيد، لكنهما يفعلان ذلك بطرق مختلفة.
عندما تركض:
git pull
يتطلب Git خطوتين: جلب بوابة لإدخال التغييرات من جهاز التحكم عن بعد، ثم دمج git لدمجها مع فرعك الحالي. قد يؤدي هذا إلى إنشاء عملية دمج إضافية إذا كانت لديك عمليات دمج محلية أعلى من العملية البعيدة، وهو ما يؤدي بدوره إلى... تاريخ يتضمن عمليات دمج غير ضرورية.
لكن إذا قمت بتشغيل:
git pull --rebase
يقوم Git بجلب البيانات ثم قم بنسخ التغييرات المحلية الخاصة بك فوق النسخة المحدثة على الخادم البعيد.والنتيجة مشابهة لما لو كنت قد فعلت git fetch seguido دي git rebase origin/mainويحافظ على سجل تاريخي أنظف وأكثر استقامة.
لهذا السبب، تقوم العديد من الفرق بضبط Git لاستخدام إعادة التأسيس (rebase) افتراضيًا عند إجراء عمليات السحب (pulls). إنها طريقة بسيطة لـ تجنب عمليات الدمج التلقائية التي لا تساهم كثيراً.
حذف الملفات أو الصور من سجل التصفح: الفكرة العامة
أحيانًا لا تكمن المشكلة في عملية إيداع سيئة، ولكن ملف ما كان ينبغي أن يصل إلى المستودع أبدًاقد يكون السبب صورة خاطئة، أو بيانات اعتماد غير صحيحة، أو ملفات ضخمة، إلخ. قد يغريك الأمر بالذهاب إلى GitHub والبحث عن أيقونة سلة المهملات، ولكن إذا ظهرت معطلة وعرضت رسائل مثل "يجب أن تكون على فرع"، فذلك لأن لا يسمح لك موقع GitHub بحذف السجل بهذه الطريقة..
تتضمن الطريقة الصحيحة عادةً استخدام إعادة كتابة سجل الأحداث من جهازك المحلي (مع إعادة التأسيس التفاعلية أو أدوات مثل بوابة تصفية الريبوثم قم بإجراء عملية دفع قسري. في GitHub Desktop، يمكنك أيضًا إدارة الفروع والالتزامات، ولكن عملية حذف ملف بالكامل من جميع عمليات الالتزام وهذا يتضمن إعادة كتابة التاريخ بطريقة مشابهة لما نراه هنا.
باختصار، إذا قمت بتحميل صورة خاطئة أو ملف حساس وتريد "أن يختفي" من سجل التغييرات، فإن حذفه في آخر عملية تثبيت لا يكفي: يجب إعادة كتابة التعديلات التي تم إدخالها فيها. ثم قم بالضغط. إنها عملية دقيقة ويجب القيام بها بهدوء وبالتنسيق مع فريقك.
تسلسل عملي للتدرب على التجاوز دون إتلاف أي شيء
أفضل طريقة لإتقان التجاوز هي التدرب في بيئة آمنة، دون الخوف من تعطيل عمل الآخرين. ويمكن اتباع تسلسل بسيط كالتالي:
- أنشئ فرعًا جديدًا من الفرع الرئيسي باستخدام git checkout -b my-branch-tests.
- قم بإجراء عدة عمليات إيداع صغيرة (يمكن أن تكون هذه تغييرات بسيطة أو ملفات اختبار).
- قم بمحاكاة تقدم المشروع الرئيسي عن طريق إجراء عمليات إيداع جديدة هناك (أو اطلب من شخص آخر القيام بذلك).
- ارجع إلى فرع الاختبار الخاص بك وقم بتشغيله git rebase main لمعرفة كيفية إعادة ترتيب عمليات الالتزام الخاصة بك.
- جرب أ git rebase -i HEAD~3 لدمج أو إعادة تسمية أو حذف أي من أحدث عمليات الالتزام.
من خلال هذا التمرين سترى كيف غيّر السجل قبل وبعدكيف يتعامل Git مع التعارضات، وكيف يمكنك استعادة الحالات السابقة باستخدام reflog عند الحاجة. كلما زادت ممارستك محليًا، زادت ثقتك عند تطبيق هذه التقنيات على فروع المشاريع الحقيقية.
يُمكّنك إتقان إعادة ترتيب التغييرات في Git من تحويل سجل التغييرات الفوضوي، المليء بالالتزامات الفارغة وعمليات الدمج غير الضرورية والرسائل غير ذات المعنى، إلى تسلسل واضح وسهل القراءة للتغييرات المهمة. باستخدام إعادة الترتيب التفاعلية، وحذف التزامات الاختبار، وتجميع العمل المتفرق، وتحديث الفروع بشكل خطي، والاعتماد على أدوات مثل reflog وعمليات الدفع القسري باستخدام `--force-with-lease`، يمكنك الحفاظ على مستودعك في حالة أكثر تنظيمًا واحترافية، شريطة أن تتذكر تطبيق هذه التقنيات فقط على الفروع التي تتحكم بها وإخطار الفريق قبل إعادة كتابة سجل التغييرات المشترك.