
التجهيز والالتزامات في Git
لا يسجل Git كل ملف تحفظه تلقائيًا. بل يمنحك طبقة تحضير تُسمّى منطقة التجهيز أو staging area، تختار من خلالها بدقة ما الذي يجب أن يحتويه الالتزام التالي.
تُعد هذه الطبقة الإضافية من أقوى ميزات Git، لكنها أيضًا من أكثر المفاهيم إرباكًا للمبتدئين. فقد يكون الملف مسجلًا في التزام، ومعدّلًا، ومجهزًا جزئيًا، ثم معدّلًا مرة أخرى في الوقت نفسه. ومن دون نموذج ذهني واضح، قد تبدو أوامر مثل git add وgit diff وgit restore وgit commit متناقضة.
يشرح هذا الدليل منطقة التجهيز بعمق، ويبني مسارًا منظمًا لإنشاء الالتزامات. ستتعلم كيف يقارن Git بين شجرة العمل وindex وأحدث التزام، وكيف تجهز ملفات كاملة أو أجزاء محددة منها، وكيف تلغي التجهيز بأمان، وكيف تُبنى الالتزامات، وكيف تكتب رسائل مفيدة، ومتى يكون تعديل الالتزام الأخير أو إعادة كتابة التاريخ مناسبًا.
الحالات الثلاث التي يجب فهمها
يتضمن مسار العمل المعتاد في Git ثلاثة تمثيلات مهمة للمشروع:
HEAD: اللقطة المحفوظة داخل الالتزام الحالي.
Index: اللقطة المقترحة للالتزام التالي، وتُسمّى عادة منطقة التجهيز.
Working tree: الملفات الظاهرة والقابلة للتعديل حاليًا على القرص.
يمكن تمثيل العلاقة بالشكل التالي:
أحدث التزام (HEAD)
|
| المقارنة بواسطة: git diff --staged
v
منطقة التجهيز (Index)
|
| المقارنة بواسطة: git diff
v
شجرة العمل
قد تحتوي كل طبقة على نسخة مختلفة من الملف نفسه.
على سبيل المثال:
يحتوي أحدث التزام على النسخة 1.
تحتوي منطقة التجهيز على النسخة 2.
تحتوي شجرة العمل على النسخة 3.
هذا ليس خطأ، بل هو ما يسمح لك بإعداد التزام دقيق مع الاستمرار في تعديل الملفات.
ما منطقة التجهيز؟
منطقة التجهيز هي اللقطة التي يقترح Git تسجيلها في الالتزام التالي. داخليًا، يخزن Git هذه المعلومات في index.
عندما تنفّذ:
git add app.php
ينسخ Git المحتوى الحالي للملف app.php إلى index. فهو لا يكتفي بتحديد اسم الملف على أنه مختار.
إذا عدّلت app.php مرة أخرى بعد تجهيزه، سيبقى index محتفظًا بالنسخة التي جرى تجهيزها سابقًا، بينما تحتوي شجرة العمل على التعديل الأحدث.
يتيح لك هذا الفرق:
فصل التغييرات غير المرتبطة في التزامات مختلفة.
مراجعة المحتوى الدقيق الذي سيحمله الالتزام التالي.
تجهيز جزء محدد من الملف فقط.
ترك التغييرات التجريبية خارج التزام مستقر.
إنشاء سجل نظيف حتى عندما يكون مجلد العمل مليئًا بتغييرات مختلطة.
لماذا يملك Git منطقة تجهيز؟
افترض أنك عدّلت ثلاثة ملفات أثناء إصلاح مشكلة في تسجيل الدخول:
يحتوي
LoginController.phpعلى إصلاح المشكلة الفعلي.يحتوي
README.mdعلى تحسين غير مرتبط في التوثيق.يحتوي
config.phpعلى تغييرات مؤقتة لتصحيح الأخطاء.
من دون منطقة تجهيز، قد تضطر إلى تسجيل التغييرات الثلاثة معًا أو التراجع يدويًا عن بعض العمل قبل الالتزام.
باستخدام Git، يمكنك تجهيز إصلاح المشكلة فقط:
git add LoginController.php
git diff --staged
git commit -m "رفض رموز تسجيل الدخول منتهية الصلاحية"
وتبقى تغييرات التوثيق وتصحيح الأخطاء داخل شجرة العمل إلى أن تقرر ما الذي ستفعله بها لاحقًا.
لذلك تعمل منطقة التجهيز كحد فاصل بين العمل الحالي والتاريخ الدائم.
الملفات المتتبعة وغير المتتبعة والمعدلة والمجهزة
تنتقل الملفات عادة بين الحالات التالية:
| الحالة | المعنى | الإجراء المعتاد |
|---|---|---|
| Untracked | الملف موجود لكنه ليس جزءًا من index أو تاريخ Git | تجهيزه أو تجاهله |
| متتبع وغير متغير | نسخة العمل مطابقة لـ index والالتزام الحالي | لا يلزم إجراء |
| Modified | نسخة العمل تختلف عن index | مراجعة العمل وتجهيز الجزء المطلوب |
| Staged | يختلف index عن الالتزام الحالي | المراجعة ثم الالتزام |
| مجهز ثم معدل مرة أخرى | يحتوي index وشجرة العمل على نسختين جديدتين مختلفتين | تحديد ما إذا كان التعديل الأخير يجب أن يُجهز أيضًا |
استخدم:
git status
للحصول على شرح تفصيلي، أو:
git status --short
للحصول على ملخص مختصر.
قراءة الحالة المختصرة بصورة صحيحة
تستخدم الصيغة المختصرة عمودين:
XY PATH
X: الفرق بين
HEADوindex.Y: الفرق بين index وشجرة العمل.
أمثلة شائعة:
?? notes.txt ملف غير متتبع
A notes.txt ملف جديد مجهز
M app.php معدل لكنه غير مجهز
M app.php النسخة المعدلة مجهزة
MM app.php نسخة مجهزة مع تغييرات أحدث غير مجهزة
D old.php حذف مجهز
R old.php -> new.php
إعادة تسمية مجهزة
للمسافات أهمية. فالحالتان M و M تصفان طبقتين مختلفتين.
إنشاء مستودع للتدرب
أنشئ مشروعًا مؤقتًا لتطبيق الأمثلة.
على macOS أو Linux أو Git Bash
mkdir git-staging-demo
cd git-staging-demo
git init -b main
cat > calculator.php <<'EOF'
<?php
function add(int $a, int $b): int
{
return $a + $b;
}
EOF
cat > README.md <<'EOF'
# Calculator Demo
مشروع صغير للتدرب على staging والالتزامات في Git.
EOF
git add calculator.php README.md
git commit -m "إنشاء مشروع تجريبي للآلة الحاسبة"
على PowerShell
New-Item -ItemType Directory git-staging-demo
Set-Location git-staging-demo
git init -b main
@"
<?php
function add(int `$a, int `$b): int
{
return `$a + `$b;
}
"@ | Set-Content calculator.php
@"
# Calculator Demo
مشروع صغير للتدرب على staging والالتزامات في Git.
"@ | Set-Content README.md
git add calculator.php README.md
git commit -m "إنشاء مشروع تجريبي للآلة الحاسبة"
تحقق من الحالة الأولية:
git status
git log --oneline
يجب أن تكون شجرة العمل نظيفة.
كيف يعمل git add؟
يحدّث git add محتوى index باستخدام البيانات الموجودة في شجرة العمل.
قد يكون اسم الأمر مضللًا قليلًا، لأنه لا يقتصر على الملفات الجديدة. فهو يجهز:
الملفات الجديدة.
التعديلات على الملفات المتتبعة.
تعارضات الدمج التي جرى حلها.
عمليات حذف الملفات عند اختيارها من خلال مسار أو خيارات تحديث مناسبة.
لتجهيز ملف واحد:
git add calculator.php
لتجهيز عدة مسارات محددة:
git add calculator.php README.md
لتجهيز مجلد:
git add src/
لتجهيز التغييرات الموجودة تحت المجلد الحالي:
git add .
لتجهيز جميع الإضافات والتعديلات وعمليات الحذف في المستودع:
git add -A
لتجهيز تعديلات وحذف الملفات المتتبعة من دون إضافة ملفات جديدة غير متتبعة:
git add -u
الفرق بين git add . وgit add -A وgit add -u
تتداخل وظائف هذه الأوامر، لكن هدف كل منها مختلف.
| الأمر | الملفات الجديدة | الملفات المعدلة | الملفات المحذوفة | النطاق |
|---|---|---|---|---|
git add . | نعم | نعم | نعم ضمن نطاق المسار المختار | المجلد الحالي وما تحته |
git add -A | نعم | نعم | نعم | المستودع كاملًا بصورة افتراضية |
git add -u | لا | نعم | نعم | المسارات المتتبعة ضمن النطاق المختار |
في كثير من المستودعات البسيطة، قد يبدو أن git add . وgit add -A يعطيان النتيجة نفسها. لكن الفرق يصبح أوضح عند تشغيل الأمر من داخل مجلد فرعي أو عندما تريد استبعاد الملفات غير المتتبعة عمدًا.
بصرف النظر عن الأمر المستخدم، راجع النتيجة المجهزة قبل الالتزام.
المراجعة قبل التجهيز
عدّل الآلة الحاسبة:
<?php
function add(int $a, int $b): int
{
return $a + $b;
}
function subtract(int $a, int $b): int
{
return $a - $b;
}
ثم نفّذ:
git status
git diff
يعرض git diff التغييرات الموجودة في شجرة العمل والتي لم تُجهز بعد.
استخدم مسارًا عندما تريد فحص ملف واحد فقط:
git diff -- calculator.php
تفصل علامتا الشرطة بين خيارات الأمر ومسارات الملفات.
المراجعة بعد التجهيز
جهّز الملف:
git add calculator.php
قد لا يعرض git diff العادي شيئًا الآن إذا لم توجد تعديلات إضافية غير مجهزة.
افحص الالتزام المقترح باستخدام:
git diff --staged
ويُعد --cached مرادفًا له:
git diff --cached
تعرض هذه المقارنة index بالنسبة إلى HEAD.
يستخدم مسار العمل المنظم المقارنتين:
git diff
git add calculator.php
git diff --staged
git commit -m "إضافة عملية الطرح"
يمكن أن يكون الملف مجهزًا ومعدلًا في الوقت نفسه
جهّز تغييرًا:
git add calculator.php
ثم عدّل الملف نفسه مرة أخرى وأضف دالة الضرب:
function multiply(int $a, int $b): int
{
return $a * $b;
}
نفّذ:
git status --short
قد ترى:
MM calculator.php
يحتوي index على تغيير الطرح، بينما تحتوي شجرة العمل على الطرح والضرب معًا.
قارن كل طبقة:
# مقارنة شجرة العمل مع index
git diff
# مقارنة index مع HEAD
git diff --staged
إذا كان الضرب يجب أن يكون جزءًا من الالتزام نفسه، فجهّز الملف مرة أخرى:
git add calculator.php
أما إذا كان ينتمي إلى التزام لاحق، فاتركه غير مجهز وسجل النسخة الموجودة حاليًا في index فقط.
التجهيز الجزئي باستخدام git add -p
قد يحتوي الملف نفسه على تعديلات منطقية غير مرتبطة. يسمح لك التجهيز الجزئي باختيار أجزاء محددة تُسمّى hunks.
نفّذ:
git add -p calculator.php
يعرض Git جزءًا واحدًا في كل مرة ويسألك ما الذي تريد فعله.
الخيارات الشائعة:
| المفتاح | المعنى |
|---|---|
y | تجهيز هذا الجزء |
n | عدم تجهيز هذا الجزء |
q | الخروج وترك الأجزاء الباقية دون قرار |
a | تجهيز هذا الجزء وجميع الأجزاء التالية في الملف |
d | تجاوز هذا الجزء وجميع الأجزاء التالية في الملف |
s | تقسيم الجزء الحالي إلى أجزاء أصغر إن أمكن |
e | تحرير patch يدويًا قبل التجهيز |
? | عرض المساعدة المتاحة |
بعد اختيار الأجزاء، افحص الجانبين:
git diff --staged
git diff
يعرض الأمر الأول ما سيتم تسجيله، ويعرض الثاني ما سيبقى خارج الالتزام.
مثال: فصل إعادة الهيكلة عن الميزة
افترض أن calculator.php يحتوي على تغييرين:
إعادة تسمية متغير لجعله أوضح.
إضافة عملية قسمة جديدة.
قد ينتمي التغييران إلى التزامين منفصلين، لأن الأول إعادة هيكلة، بينما يغيّر الثاني سلوك البرنامج.
استخدم:
git add -p calculator.php
جهّز إعادة التسمية فقط، ثم أنشئ الالتزام:
git diff --staged
git commit -m "توضيح أسماء معاملات الآلة الحاسبة"
تبقى شيفرة القسمة غير مجهزة. جهّزها وسجلها بعد ذلك:
git add calculator.php
git diff --staged
git commit -m "إضافة عملية القسمة"
ينتج عن ذلك تاريخ أسهل في المراجعة والتراجع.
التجهيز التفاعلي باستخدام git add -i
يوفر Git أيضًا واجهة تفاعلية للتجهيز:
git add -i
يمكن أن تساعدك على:
عرض الحالة.
إضافة التحديثات.
إلغاء اختيارات التجهيز.
إضافة الملفات غير المتتبعة.
تجهيز patch.
فحص الفروقات.
يستخدم كثير من المطورين git add -p بصورة أكبر لأنه ينتقل مباشرة إلى اختيار الأجزاء، لكن -i مفيد عند الحاجة إلى تنسيق عدة عمليات تجهيز.
Intent to Add
الخيار:
git add -N new-file.php
أو:
git add --intent-to-add new-file.php
يسجل أن المسار سيُضاف لاحقًا من دون تجهيز محتواه الكامل كإضافة عادية.
قد يجعل ذلك الملف الجديد ظاهرًا داخل مسارات العمل المعتمدة على diff قبل تجهيز محتواه النهائي. وهو خيار متقدم وغير ضروري للمسارات الأساسية.
ما الذي يسجله الالتزام؟
يسجل الالتزام اللقطة المجهزة في index ويربطها بالتاريخ الموجود.
يتضمن كائن الالتزام معلومات مثل:
مرجع إلى شجرة المستوى الأعلى التي تمثل اللقطة.
التزام أب واحد أو أكثر، باستثناء الالتزام الأول.
هوية المؤلف ووقت التأليف.
هوية منشئ الالتزام ووقت الالتزام.
رسالة الالتزام.
ثم يمنح Git الالتزام معرّف كائن فريدًا.
لا يحتوي الالتزام على كل تعديل غير مجهز في شجرة العمل، بل يسجل index فقط.
الفرق بين Author وCommitter
يخزن Git كلًا من المؤلف ومنشئ الالتزام.
Author: الشخص الذي أنشأ التغيير في الأصل.
Committer: الشخص الذي سجل هذه النسخة من الالتزام داخل التاريخ الحالي.
يكونان عادة الشخص نفسه أثناء العمل المحلي العادي.
وقد يختلفان عندما:
يطبّق maintainer تعديلًا أرسله مطور آخر.
يُعاد تأسيس الالتزام باستخدام rebase.
تعيد cherry-pick إنشاء التغيير داخل تاريخ مختلف.
يعدل شخص آخر الالتزام باستخدام amend.
افحص القيمتين باستخدام:
git log -1 --format=fuller
إنشاء التزام
سجل المحتوى المجهز برسالة مباشرة:
git commit -m "إضافة عملية الطرح"
أو افتح المحرر المضبوط:
git commit
يفيد استخدام المحرر عندما تحتاج الرسالة إلى عنوان قصير يليه شرح تفصيلي.
قبل الالتزام، افحص:
git status
git diff --staged
بعد الالتزام، افحص:
git status
git log -1 --stat
كتابة رسائل التزام قوية
يجب أن توضح رسالة الالتزام الغرض من التغيير المسجل.
أمثلة ضعيفة:
update
changes
fix stuff
new code
final version
أمثلة أقوى:
رفض القسمة على صفر
التحقق من صيغة البريد قبل التسجيل
إزالة إعادة محاولة الدفع المكررة
توثيق إعداد قاعدة البيانات المحلية
تحدد الرسائل الأقوى إجراءً مفيدًا وتجعل التاريخ قابلًا للبحث.
بنية عملية لرسالة الالتزام
في الالتزام البسيط، يكفي غالبًا عنوان واضح واحد:
إضافة عملية الطرح
أما في التغيير المعقد، فاستخدم:
إضافة فحص انتهاء صلاحية رموز إعادة تعيين كلمة المرور
رفض الرموز الأقدم من 30 دقيقة قبل تحميل المستخدم.
يمنع ذلك وصول روابط إعادة التعيين المنتهية إلى نموذج كلمة المرور.
من القواعد الشائعة:
اكتب عنوانًا مختصرًا.
استخدم أسلوبًا موجّهًا إلى الفعل مثل: أضف، أصلح، أزل.
اترك سطرًا فارغًا قبل متن الرسالة.
اشرح لماذا كان التغيير مطلوبًا، وأضف سياق التنفيذ المهم.
تجنب تكرار معلومات يوضحها diff أصلًا.
قد تفرض قواعد المستودع أو المؤسسة متطلبات تنسيق إضافية.
سجل سبب التغيير، لا ما تغيّر فقط
يوضح diff الأسطر التي تغيرت. أما الرسالة فيجب أن تحفظ السبب الذي قد لا يكون واضحًا لاحقًا.
أقل فائدة:
تغيير المهلة من 10 إلى 30
أكثر فائدة:
زيادة مهلة callback للدفع لدى المزودين البطيئين
يحتاج بعض المزودين إلى أكثر من عشر ثوانٍ أثناء الضغط.
تمنع مهلة الثلاثين ثانية إعادة المحاولة المبكرة من دون تجاوز حد API.
تحفظ الرسالة الثانية السبب التشغيلي وراء الرقم.
قوالب رسائل الالتزام
يمكن للفريق توفير قالب رسالة يحتوي على تذكيرات للمؤلفين.
أنشئ قالبًا مثل:
# الملخص
# لماذا نحتاج هذا التغيير؟
# كيف جرى اختباره؟
# المشكلة المرتبطة:
ثم اضبطه:
git config --global commit.template ~/.gitmessage.txt
عندما يفتح git commit المحرر، يساعد القالب المطورين على إضافة سياق متسق.
تُحذف الأسطر التي تُعامل كتعليقات عادة وفق آلية تنظيف الرسائل في Git.
مراجعة الالتزام من دون إنشائه
استخدم التشغيل التجريبي:
git commit --dry-run
يعرض هذا الأمر ما الذي سيُضمّن وما الذي سيبقى خارج الالتزام، من دون إنشاء الالتزام.
يمكن دمجه مع صيغة الحالة المختصرة:
git commit --dry-run --short
لكن أكثر طريقة موثوقة لمراجعة المحتوى تظل:
git diff --staged
تسجيل تغييرات الملفات المتتبعة باستخدام git commit -a
الاختصار:
git commit -a -m "تحديث عمليات الآلة الحاسبة"
يجهز تلقائيًا التعديلات وعمليات الحذف في الملفات المتتبعة، ثم ينشئ الالتزام.
ولا يضيف عادة الملفات الجديدة غير المتتبعة.
قد يكون هذا الاختصار مفيدًا في تغيير صغير ومحدد، لكنه يتجاوز خطوة المراجعة الصريحة باستخدام git add، وقد يضم تعديلات نسيت وجودها.
التسلسل الأكثر أمانًا:
git status
git diff
git add -u
git diff --staged
git commit -m "تحديث عمليات الآلة الحاسبة"
سجل المسارات المختارة بحذر
يدعم Git صيغ التزام تسمي المسارات مباشرة، لكنها قد تتصرف بصورة مختلفة عن المسار العادي المعتمد على index.
للوضوح والمراجعة المتوقعة، خصوصًا أثناء التعلم، استخدم:
git add path/to/file
git diff --staged
git commit -m "صف التغيير"
يجعل ذلك اللقطة المقترحة مرئية قبل تحديث التاريخ.
كيفية إلغاء تجهيز ملف
يمكن التراجع عن التجهيز. إذا أضفت ملفًا إلى index بالخطأ، يمكنك إلغاء تجهيزه من دون حذف التغييرات الموجودة في شجرة العمل.
استخدم:
git restore --staged calculator.php
يعيد هذا الأمر نسخة index من HEAD بصورة افتراضية، مع إبقاء ملف العمل كما هو.
تحقق من النتيجة:
git status
git diff
git diff --staged
يُفترض أن تنتقل التغييرات من مقارنة staged إلى مقارنة unstaged.
صيغة reset القديمة
قد تصادف أيضًا:
git reset HEAD calculator.php
عند استخدامه مع مسار بهذه الطريقة، يحدّث index ويحافظ غالبًا على تعديلات شجرة العمل. ويوفر Git الحديث git restore --staged باعتباره أمرًا أوضح لهذه المهمة.
إلغاء تجهيز جزء من التغييرات
استخدم وضع patch:
git restore --staged -p calculator.php
يسمح لك هذا الأمر بإزالة أجزاء محددة من index مع إبقاء أجزاء أخرى مجهزة.
وهو النظير العكسي للتجهيز الجزئي:
يختار
git add -pالأجزاء التي ستُضاف إلى index.يختار
git restore --staged -pالأجزاء التي ستُزال من index.
إلغاء التجهيز لا يعني حذف التغييرات
الأمر:
git restore --staged calculator.php
يغيّر index لكنه يحافظ على تعديل شجرة العمل.
أما الأمر:
git restore calculator.php
فيعيد ملف العمل من index بصورة افتراضية، وقد يحذف التغييرات غير المجهزة.
قبل استعادة ملف في شجرة العمل، افحص:
git diff -- calculator.php
قد لا يكون من الممكن استعادة التغييرات غير المسجلة بعد تنفيذ restore مدمّر.
تعديل أحدث التزام باستخدام amend
إذا احتوى أحدث التزام محلي على خطأ، يمكنك استبداله بالتزام معدل.
تشمل الحالات الشائعة:
نسيان ملف مطلوب.
وجود خطأ في رسالة الالتزام.
انتماء تصحيح صغير منطقيًا إلى الالتزام نفسه.
إضافة ملف منسي
git add forgotten-test.php
git commit --amend --no-edit
يحافظ --no-edit على الرسالة الحالية.
تغيير رسالة الالتزام الأخير
git commit --amend
عدّل الرسالة، واحفظ الملف، ثم أغلق المحرر.
تعديل الالتزام برسالة مباشرة جديدة
git commit --amend -m "إضافة اختبارات الطرح والتحقق"
ينشئ amend كائن التزام جديدًا. ويتغير معرّف الالتزام لأن اللقطة أو البيانات الوصفية تغيرت.
لا تعدّل التاريخ المشترك دون حذر
يؤدي تعديل التزام جرى رفعه واستخدامه من قبل الآخرين إلى إعادة كتابة التاريخ. وقد يتطلب تحديث المستودع البعيد force push، ما قد يربك المتعاونين.
قاعدة عملية:
استخدم amend بحرية عندما يكون الالتزام خاصًا ومحليًا.
كن حذرًا بعد نشره.
اتبع سياسة الفريق للفروع المشتركة.
فضّل إنشاء التزام تصحيحي جديد عندما يكون التاريخ مستخدمًا بالفعل.
الالتزامات الفارغة
يوقف Git العملية عادة عندما لا توجد تغييرات مجهزة.
يمكن إنشاء التزام فارغ صراحة باستخدام:
git commit --allow-empty -m "تشغيل مسار النشر"
تشمل الاستخدامات المحتملة:
تشغيل أتمتة مرتبطة بأحداث الالتزام.
تسجيل حدث في المشروع من دون تغيير المحتوى المتتبع.
اختبار تكاملات المستودع.
يجب أن تكون الالتزامات الفارغة مقصودة وموضحة بوضوح.
الفرق بين Sign-Off والتوقيع التشفيري
هذان مفهومان مختلفان.
Developer Sign-Off
الخيار:
git commit --signoff -m "إضافة عملية القسمة"
يضيف سطر Signed-off-by باستخدام هوية منشئ الالتزام. وقد تستخدمه المشاريع لتسجيل الموافقة على سياسة مساهمة مثل Developer Certificate of Origin.
التوقيع التشفيري للالتزام
يستخدم الالتزام الموقّع تشفيريًا إعدادات ومفاتيح توقيع لإرفاق توقيع قابل للتحقق:
git commit -S -m "إضافة عملية القسمة"
يتطلب ذلك إعدادًا مسبقًا، وهو منفصل عن سطر sign-off النصي.
لا تصف --signoff بأنه تحقق تشفيري.
Commit Trailers
يمكن أن تظهر بيانات وصفية منظمة في نهاية رسالة الالتزام:
إصلاح إنشاء الفاتورة المكرر
منع عامل إعادة المحاولة من إنشاء فاتورة بعد أن يكون الطلب
الأول قد اكتمل بنجاح بالفعل.
Reported-by: Jane Developer <jane@example.com>
Reviewed-by: Alex Maintainer <alex@example.com>
تحدد بعض المشاريع trailers وسياسات آلية خاصة بها. استخدم فقط ما يطلبه المشروع أو يتعرف إليه.
التزامات Fixup وSquash
أثناء التطوير المحلي، قد تكتشف أن تغييرًا جديدًا ينتمي إلى التزام أقدم.
أنشئ التزام fixup:
git add calculator.php
git commit --fixup=<target-commit>
ينشئ Git رسالة تبدأ بـ fixup!. ويمكن لاحقًا دمجها مع الالتزام المستهدف باستخدام rebase تفاعلي وautosquash.
يمكن لالتزام squash الاحتفاظ برسالة إضافية:
git commit --squash=<target-commit>
تعيد هذه المسارات كتابة التاريخ، ولذلك يُفضّل استخدامها قبل مشاركة الالتزامات على نطاق واسع.
سيجري شرحها بتفصيل أكبر في مقالات مسار العمل وإعادة كتابة التاريخ.
Commit Hooks والتحقق
خطافات Git هي برامج تعمل في نقاط محددة من عمليات Git.
يمكن استخدام الخطافات المرتبطة بالالتزام لتنفيذ مهام مثل:
تنسيق الملفات المجهزة.
تشغيل الاختبارات.
رفض الأسرار.
فحص بنية رسالة الالتزام.
إضافة البيانات الوصفية أو التحقق منها.
من أمثلتها:
pre-commitprepare-commit-msgcommit-msgpost-commit
يمكن لخطاف محلي منع الالتزام، لكن لا ينبغي الاعتماد على الخطافات المحلية وحدها بوصفها طبقة الحماية الوحيدة، لأنها قد لا تكون مثبتة أو مفعلة على كل جهاز. يجب تشغيل الفحوص المهمة أيضًا داخل CI مشترك أو مسارات server-side.
مسار كامل لإنشاء التزامات نظيفة
استخدم التسلسل التالي في معظم أعمالك اليومية:
1. فحص المستودع
git status
2. مراجعة التغييرات غير المجهزة
git diff
3. تجهيز تغيير منطقي واحد
git add path/to/file
أو اختيار الأجزاء:
git add -p
4. التحقق من اللقطة المقترحة
git diff --staged
git status
5. تشغيل الاختبارات المناسبة
# مثال فقط؛ استخدم أمر الاختبار الفعلي في المشروع
php vendor/bin/phpunit
6. إنشاء التزام برسالة مفيدة
git commit -m "رفض القسمة على صفر"
7. التحقق من النتيجة
git status
git log -1 --stat
8. تكرار العملية للعمل المتبقي
إذا استمر git status في عرض تعديلات غير مرتبطة، فجهز الالتزام المحدد التالي بدل دمج كل شيء تلقائيًا.
تطبيق يدوي: فصل تغييرين داخل ملف واحد
يعرض هذا المثال التجهيز الجزئي من البداية إلى النهاية.
الملف المسجل أولًا
<?php
function add(int $a, int $b): int
{
return $a + $b;
}
نسخة شجرة العمل
<?php
function add(int $left, int $right): int
{
return $left + $right;
}
function subtract(int $left, int $right): int
{
return $left - $right;
}
يوجد تغييران منطقيان:
إعادة تسمية المعاملين
$aو$bإلى أسماء أوضح.إضافة سلوك الطرح.
الفحص
git diff -- calculator.php
تجهيز الأجزاء المختارة
git add -p calculator.php
اختر إعادة تسمية المعاملات فقط. قسّم الجزء باستخدام s إذا استطاع Git تقسيمه. لا تستخدم تحرير patch يدويًا إلا إذا كنت تفهم الصيغة المعروضة.
التحقق من الجانبين
git diff --staged
git diff
يجب أن يعرض staged diff إعادة التسمية، بينما يعرض unstaged diff دالة الطرح الجديدة.
إنشاء الالتزام الأول
git commit -m "توضيح أسماء معاملات الآلة الحاسبة"
تجهيز الميزة المتبقية
git add calculator.php
git diff --staged
إنشاء الالتزام الثاني
git commit -m "إضافة عملية الطرح"
فحص التاريخ
git log --oneline --stat -2
النتيجة هي التزامان مفهومَان بدل لقطة واحدة مختلطة.
تطبيق يدوي: إلغاء التجهيز دون فقدان العمل
افترض أن الملفين جرى تجهيزهما بالخطأ:
git add calculator.php README.md
git status --short
الحالة المتوقعة:
M README.md
M calculator.php
ألغِ تجهيز README:
git restore --staged README.md
افحص مجددًا:
git status --short
الشكل المتوقع:
M README.md
M calculator.php
لا يزال تغيير README موجودًا داخل شجرة العمل، ولم يُحذف.
سجل تغيير الآلة الحاسبة فقط:
git diff --staged
git commit -m "إضافة التحقق إلى الآلة الحاسبة"
يبقى README متاحًا لالتزام آخر.
تطبيق يدوي: تعديل التزام لإضافة اختبار منسي
أنشئ التزامًا:
git add calculator.php
git commit -m "رفض القسمة على صفر"
ثم لاحظ أن الاختبار المرتبط لم يُضمّن:
git add tests/CalculatorTest.php
git diff --staged
git commit --amend --no-edit
افحص الالتزام المعدّل:
git show --stat
git log -1 --format=fuller
يتضمن الالتزام المعدّل الآن التنفيذ والاختبار، ويختلف معرّفه عن الالتزام الأصلي.
أخطاء شائعة في التجهيز
تجهيز كل شيء دون مراجعة
قد يضم git add . ملفات تصحيح الأخطاء، والمخرجات المولدة، والتعديلات غير المرتبطة، أو بيانات الاعتماد.
افتراض أن git add يتتبع التعديلات المستقبلية تلقائيًا
يجهز Git محتوى الملف في اللحظة التي يُنفذ فيها الأمر. ويجب تجهيز التعديلات اللاحقة مرة أخرى إذا كانت تنتمي إلى الالتزام نفسه.
استخدام git status وحده
تعرض الحالة مستوى الملفات، لكنها لا تعرض المحتوى الكامل سطرًا بسطر. راجع diff أيضًا.
الخلط بين إلغاء التجهيز وحذف التغييرات
يحدّث git restore --staged index. أما git restore من دون --staged فقد يستبدل تغييرات العمل.
استخدام git commit -a مع الملفات الجديدة
يتعامل هذا الاختصار مع تعديلات وحذف الملفات المتتبعة، ولا يضيف الملفات غير المتتبعة العادية.
إنشاء التزامات مختلطة
لا ينبغي أن يجمع الالتزام الواحد إعادة هيكلة وميزات وتصحيح أخطاء وتوثيق غير مرتبطة، إلا إذا كانت تشكل تغييرًا واحدًا لا يمكن فصله.
تعديل التزام منشور دون تنسيق
يغيّر amend معرّف الالتزام، وقد يتطلب push يعيد كتابة التاريخ.
اعتبار Sign-Off توقيعًا تشفيريًا
يخدم سطر Signed-off-by النصي والتوقيع التشفيري القابل للتحقق غرضين مختلفين.
أخطاء شائعة في رسائل الالتزام
استخدام عناوين غامضة مثل
update.وصف أسماء الملفات فقط بدل الغرض من التغيير.
جمع تغييرات غير مرتبطة تحت رسالة واحدة.
كتابة متن طويل من دون ملخص واضح في السطر الأول.
نسخ عناوين التذاكر التي لا تشرح التغيير التقني.
إضافة معلومات حساسة إلى الرسالة.
استخدام قواعد غير متسقة داخل المستودع نفسه.
أفضل الممارسات للالتزامات النظيفة
ابدأ باستخدام
git status.راجع العمل غير المجهز قبل اختياره.
استخدم التجهيز حسب المسار أو patch عندما تكون التغييرات مختلطة.
راجع
git diff --stagedفي كل مرة.شغّل اختبارات محددة قبل الالتزام.
اجعل كل التزام قابلًا للبناء عندما يكون ذلك عمليًا.
اكتب عنوانًا واضحًا يصف التغيير.
استخدم متن الرسالة لحفظ الأسباب والقيود.
عدّل التاريخ الخاص فقط ما لم يتفق الفريق على غير ذلك.
أبقِ الأسرار والملفات المولدة خارج index.
تحقق من الحالة النهائية بعد كل التزام.
مرجع الأوامر المفيدة
| الأمر | الغرض |
|---|---|
git status | شرح حالة شجرة العمل وindex الحالية |
git status --short | عرض رموز الحالة المختصرة بعمودين |
git diff | مراجعة التغييرات غير المجهزة |
git diff --staged | مراجعة الالتزام التالي المقترح |
git add file | تجهيز المحتوى الحالي لملف واحد |
git add -p | تجهيز الأجزاء المختارة بصورة تفاعلية |
git add -i | فتح واجهة التجهيز التفاعلية |
git add -u | تجهيز تعديلات وحذف الملفات المتتبعة |
git add -A | تجهيز الإضافات والتعديلات والحذف |
git restore --staged file | إلغاء تجهيز مسار مع الحفاظ على تغييرات العمل |
git restore --staged -p | إلغاء تجهيز أجزاء مختارة |
git commit | إنشاء التزام من index باستخدام المحرر |
git commit -m "message" | إنشاء التزام برسالة مباشرة |
git commit --dry-run | معاينة ما سيُضمّن من دون إنشاء الالتزام |
git commit -a | تجهيز تعديلات وحذف الملفات المتتبعة ثم الالتزام |
git commit --amend | استبدال أحدث التزام |
git commit --amend --no-edit | استبدال أحدث التزام مع إبقاء رسالته |
git log -1 --format=fuller | فحص بيانات author وcommitter |
قائمة التحقق للتجهيز والالتزام
قبل إنشاء الالتزام، تحقق من:
الفرع الحالي صحيح.
الملفات المجهزة تنتمي إلى تغيير منطقي واحد.
لا توجد بيانات اعتماد أو معلومات خاصة مجهزة.
الملفات المولدة مستبعدة عندما يكون ذلك مناسبًا.
تطابق
git diff --stagedمع هدفك.نجاح الاختبارات ذات الصلة.
وضوح عنوان الالتزام.
حفظ متن الرسالة للأسباب المهمة عند الحاجة.
بعد الالتزام، تحقق من:
وجود الالتزام داخل السجل.
صحة معلومات المؤلف.
وجود الملفات المقصودة داخل الالتزام.
فهم التغييرات المتبقية في شجرة العمل.
نظافة المستودع عند اكتمال المهمة.
الأسئلة الشائعة
هل منطقة التجهيز مطلوبة؟
يُعد index جزءًا أساسيًا من نموذج اللقطات المعتاد في Git. تستطيع بعض خيارات الالتزام اختيار مسارات متتبعة مباشرة، لكن مسار التجهيز ثم المراجعة ثم الالتزام أوضح وأكثر أمانًا في معظم حالات التطوير.
هل يحفظ git add عملي بصورة دائمة؟
لا. فهو يجهز المحتوى داخل index. أما الالتزام فيسجل تلك اللقطة داخل تاريخ المستودع.
لماذا لا يعرض git diff شيئًا؟
قد تكون تغييراتك مجهزة بالفعل. شغّل git diff --staged. وقد تكون شجرة العمل نظيفة أيضًا.
هل يمكن تجهيز جزء من ملف فقط؟
نعم. استخدم git add -p path/to/file واختر الأجزاء المطلوبة.
هل يمكن إلغاء التجهيز من دون فقدان التغييرات؟
نعم. استخدم git restore --staged path/to/file.
ما معنى MM في git status --short؟
يعني أن الملف يحتوي على تعديل مجهز وتعديل إضافي غير مجهز.
هل يضم git commit الملفات غير المتتبعة؟
لا، إلا إذا جرى تجهيزها أولًا. وحتى git commit -a لا يضيف الملفات غير المتتبعة عادة.
هل يجب أن يكون كل التزام صغيرًا؟
يجب أن يكون الالتزام محدد الهدف، لا صغيرًا بصورة مصطنعة. وقد يعدل عددًا كبيرًا من الملفات إذا كانت جميعها مطلوبة لتغيير واحد متماسك.
هل يمكن تغيير أحدث التزام؟
نعم باستخدام git commit --amend. تجنب تعديل التاريخ المشترك من دون تنسيق.
لماذا تغير hash الالتزام بعد amend؟
ينشئ amend التزامًا بديلًا. ولأن محتواه أو بياناته الوصفية مختلفة، يحصل على معرّف كائن مختلف.
هل Signed-off-by توقيع موثق؟
لا. هو سطر نصي. يستخدم التوقيع التشفيري مفاتيح توقيع وخيارات مثل -S.
الخلاصة
منطقة التجهيز ليست عقبة غير ضرورية بين التعديل والالتزام، بل هي الآلية التي تتيح لك تصميم اللقطة التالية بوعي.
تحتوي شجرة العمل على التعديلات الحالية، ويحتوي index على الالتزام المقترح، بينما يمثل HEAD أحدث لقطة مسجلة. وعندما يصبح هذا النموذج الثلاثي واضحًا، تصبح أوامر status وdiff متوقعة.
مسار الالتزام الاحترافي بسيط: افحص المستودع، وراجع تغييرات العمل، وجهّز وحدة منطقية واحدة، وافحص staged diff، وشغّل الاختبارات المناسبة، وأنشئ التزامًا واضحًا، ثم تحقق من الحالة النهائية.
لا ينتج التاريخ النظيف عن كثرة الالتزامات أو استخدام أوامر معقدة. بل ينتج عن اتخاذ قرارات واضحة بشأن التغييرات التي تنتمي معًا، وحفظ السبب وراء كل تغيير.
ستركز المقالة التالية على تاريخ Git والفروقات وأوامر المساعدة، بما يشمل فلاتر git log المتقدمة، وفحص الالتزامات، وتاريخ الملفات، وblame، وصيغ revisions، وتقنيات العثور على وقت دخول تغيير معين إلى المشروع.
المراجع الرسمية ومواد إضافية للقراءة

