كيف يسهّل Git Bisect عملية تصحيح الأخطاء بفعالية فائقة

دقائق القراءة: 6

مقدمة: تبسيط عملية تصحيح الأخطاء باستخدام Git Bisect

يُعد Git Bisect أداة رائعة ضمن نظام التحكم بالإصدارات Git، مصممة لجعل عملية تصحيح الأخطاء (Debugging) أسهل بكثير. ومع ذلك، لا يستخدمها الكثيرون بشكل فعال. في هذا المقال السريع والعميق، سنستعرض كيف يمكن لـ Git Bisect أن يشير بسهولة إلى مكان وجود الأخطاء في مشروعك، مما يوفر عليك ساعات من البحث اليدوي.

فهم منهجية Delta Debugging: البحث الثنائي للأخطاء

قبل الغوص في تفاصيل Git Bisect، دعنا نتحدث عن المبدأ الأساسي الذي يستند إليه: Delta Debugging. هذه المنهجية هي عملية تتضمن إكمال العديد من خطوات تصحيح الأخطاء، وفي كل خطوة يكون هدفك هو إزالة نصف "المشكلة". يمكنك التفكير في الأمر كبحث ثنائي (Binary Search) لتحديد الأخطاء.

كما قال أندرياس زيلر (Andreas Zeller)، الذي صاغ هذا المصطلح: "تُمكن Delta Debugging من أتمتة المنهج العلمي لتصحيح الأخطاء." الفكرة الأساسية للمنهج العلمي هي وضع فرضية حول سبب عدم عمل شيء ما. تقوم باختبار هذه الفرضية، ثم تقوم بتحسينها أو رفضها بناءً على نتيجة الاختبار. عند تصحيح الأخطاء، يقوم المطورون بهذا طوال الوقت، ولكن بشكل يدوي. Delta Debugging تعمل على أتمتة هذه العملية.

Git Bisect هي الطريقة التي نطبق بها Delta Debugging مع Git. لنفترض أن لدينا خطأ ونحاول العثور على السبب الجذري له. في كل خطوة من خطوات تحقيقنا عن حل، نقوم بإزالة نصف مساحة الحل المحتملة، سواء كانت تتعلق بالتكوين (Configuration)، أو الكود (Code)، أو المدخلات (Input)، أو أي شيء آخر.

مثال عملي: استخدام Git Bisect لتحديد خطأ في الكود

لجعل الأمر أكثر وضوحًا، دعنا نتبع مثالًا عمليًا خطوة بخطوة.

1. تهيئة المستودع (Repository)

أولاً، نحتاج إلى تهيئة مستودع لتتبع عملنا. سنقوم بإنشاء مجلد جديد وتهيئته كمستودع Git:

mkdir test_git_bisect && cd test_git_bisect && git init

2. إنشاء سكربت تحويل التوقيت

لنفترض أننا سنقوم بإنشاء سكربت يحصل على توقيت Epoch ويحوله إلى صيغة datetime قابلة للقراءة. سنفعل ذلك باستخدام ملف إدخال (يُسمى epochs.txt) يجب أن يحتوي على توقيتات Epoch فقط. يرجى ملاحظة أنه لتشغيل Git Bisect بسلاسة، نحتاج إلى عدد لا بأس به من الالتزامات (Commits).

السكربت بايثون parse_epochs.py الذي سنستخدمه هنا بسيط للغاية:

from time import localtime, strftime

with open('epochs.txt', 'r') as handler:
    epochs = handler.readlines()

for epoch in epochs:
    current_datetime = strftime('%Y-%m-%d %H:%M:%S', localtime(int(epoch)))
    print(current_datetime)

3. الالتزام الأول: إنشاء محلل التوقيت

لنلتزم بالتغيير الأول، وهو إنشاء السكربت:

git add . && git commit -m "Created epoch parser"

4. إنشاء ملف الإدخال الأولي

الآن، لنقم بإنشاء ملف الإدخال epochs.txt الذي سيحتوي على توقيتات Epoch صحيحة:

for i in {1..100}; do sleep 3; date +%s >> epochs.txt; done

هذا الأمر يقوم بإنشاء 100 سطر، كل سطر يحتوي على توقيت Epoch، مع فاصل زمني قدره 3 ثوانٍ بين كل توقيت وآخر. هذا يمثل جميع التوقيتات من وقت بدء السكربت (بالإضافة إلى 3 ثوانٍ) حتى حوالي خمس دقائق لاحقًا.

5. الالتزام الثاني: إضافة ملف الإدخال

مرة أخرى، نلتزم بالتغيير:

git add . && git commit -m "Generated the first version of input"

6. تشغيل السكربت بنجاح

إذا قمنا الآن بتشغيل السكربت الأولي، فسنحصل على جميع المدخلات التي تم تحليلها إلى تواريخ بنجاح:

$ python3 parse_epochs.py
2020-07-21 16:08:39
2020-07-21 16:10:40
2020-07-21 16:10:43
2020-07-21 16:10:46
2020-07-21 16:10:49
2020-07-21 16:10:52
...
(مخرجات أخرى صحيحة)

7. إدخال بيانات خاطئة (إحداث الخطأ)

الآن، لنعدّل ملف الإدخال epochs.txt لجعله معيبًا عن طريق إضافة سطر نصي غير رقمي:

echo "random string" >> epochs.txt

8. الالتزام الثالث: إضافة مدخلات خاطئة

ونلتزم مرة أخرى بهذا التغيير:

git add . && git commit -m "Added faulty input"

9. إضافة المزيد من المدخلات الخاطئة (لزيادة التعقيد)

من أجل زيادة تعقيد المثال، دعنا نضيف المزيد من المدخلات الخاطئة في التزامات منفصلة:

echo "This is not an epoch" >> epochs.txt && git add . && git commit -m "Added faulty input v2"
echo "Stop this, the script will break" >> epochs.txt && git add . && git commit -m "Added faulty input v3"

10. سجل الالتزامات (Commit Log)

إليك سجل الالتزامات الذي أنشأناه حتى الآن:

$ git log --pretty=format:"%h - %an, %ar : %s"
b811d35 - Periklis Gkolias, 2 minutes ago: Added faulty input v3
dbf75cd - Periklis Gkolias, 2 minutes ago: Added faulty input v2
cbfa2f5 - Periklis Gkolias, 8 minutes ago: Added faulty input
d02eae8 - Periklis Gkolias, 20 minutes ago: Generated first version of input
a969f3d - Periklis Gkolias, 26 minutes ago: Created epoch parser

11. تشغيل السكربت (الفشل المتوقع)

إذا قمنا بتشغيل السكربت مرة أخرى الآن، فمن الواضح أنه سيفشل مع الخطأ التالي، لأننا أدخلنا نصًا لا يمكن تحويله إلى عدد صحيح:

Traceback (most recent call last):
  File "parse_epochs.py", line 6, in <module>
    current_datetime = strftime('%Y-%m-%d %H:%M:%S', localtime(int(epoch)))
ValueError: invalid literal for int() with base 10: 'random string\n'

يبدو أننا بحاجة إلى Git Bisect لإصلاح هذا الخطأ.

استخدام Git Bisect لتحديد الالتزام المسبب للخطأ

للقيام بذلك، نحتاج إلى بدء عملية التحقيق باستخدام Git Bisect:

1. بدء عملية البحث

git bisect start

2. تحديد الالتزام السيئ والجيد

نحدد التزامًا واحدًا على أنه سيئ (bad) (عادةً ما يكون الأخير الذي يحتوي على الخطأ) والتزامًا واحدًا على أنه جيد (good) (التزام نعرف أنه كان يعمل بشكل صحيح). في مثالنا، الالتزام الأخير b811d35 هو السيئ، والالتزام d02eae8 (عندما أنشأنا المدخلات الصحيحة لأول مرة) هو الجيد:

git bisect bad b811d35 && git bisect good d02eae8

بعد ذلك، سيقوم Git Bisect بتقسيم سجل الالتزامات بين الالتزام الجيد والسيئ إلى نصفين، ويقوم بالتحقق من التزام وسيط. يمكنك رؤية الالتزامات التي يعتبرها المتسببين المحتملين عن طريق git bisect visualize (إذا كان لديك واجهة رسومية)، أو ببساطة عرض الالتزام الذي تم التحقق منه حاليًا باستخدام git show:

git show
(سيُظهر تفاصيل الالتزام dbf75cd)

في حالتنا، سيُظهر الالتزام dbf75cd.

3. اختبار الالتزامات الوسيطة وتحديدها

إذا قمنا بتشغيل السكربت الآن (بعد أن قام Git Bisect بالتحقق من dbf75cd)، فسيظل يفشل. لذا، نحدد الالتزام الحالي على أنه سيئ:

git bisect bad dbf75cd

من الجدير بالذكر مخرجات Git في هذه الحالة:

git bisect bad dbf75cd
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[cbfa2f5f52b7e8a0c3a510a151ac7653377cfae1] Added faulty input

يعرف Git أننا على وشك الوصول إلى الالتزام المسبب للخطأ! إذا قمنا بتشغيل السكربت مرة أخرى، فإنه بالطبع سيفشل. وإذا قمنا بتحديد هذا الالتزام على أنه سيئ، فإن Git سيُعلن:

git bisect bad cbfa2f5
cbfa2f5f52b7e8a0c3a510a151ac7653377cfae1 is the first bad commit

لقد وجدنا الالتزام الأول الذي أدخل الخطأ!

4. الحصول على تفاصيل الالتزام المسبب للخطأ

بحلول هذه المرحلة، يمكنك إما إصلاح الخطأ بنفسك أو الاتصال بمن قام بالالتزام بالكود/الإدخال/التكوين السيئ. إليك كيفية الحصول على تفاصيل هذا الالتزام:

$ git show -s --format='%an, %ae' cbfa2f5
Periklis Gkolias, myemail@domain.com

الخلاصة التقنية

يُعد Git Bisect أداة لا غنى عنها في ترسانة أي مطور جاد، حيث يجسد مبدأ Delta Debugging بكفاءة عالية. بدلًا من التخمين أو التراجع اليدوي عبر سجل الالتزامات، يوفر Git Bisect منهجية منظمة ومؤتمتة لتحديد الالتزام الدقيق الذي أدخل الخطأ. هذه القدرة على عزل السبب الجذري للمشكلة بسرعة فائقة لا توفر الوقت والجهد فحسب، بل تعزز أيضًا جودة الكود وتسرع دورة التطوير. إن فهم واستخدام Git Bisect بانتظام يمكن أن يحول عملية تصحيح الأخطاء من مهمة شاقة إلى عملية فعالة ومجزية.

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *