Git Flow وGitHub Flow وtrunk-based: أي سير عمل تختار في Git
Git Flow نموذج تفريع بفروع دائمة main وdevelop وفروع مؤقتة feature وrelease وhotfix، ويناسب المنتجات ذات الإصدارات المخططة. أما مواقع الويب التي تُنشر عدة مرات يومياً فالأبسط لها GitHub Flow (فروع قصيرة من main وpull request) أو trunk-based development مع feature flags.
ما هو سير عمل Git ولماذا يهم اختياره
سير عمل Git (Git workflow) اتفاق بين أعضاء الفريق على الفروع الموجودة، ومن أين تُنشأ، وأين تُدمج، وما الذي يُعتبر جاهزاً للإصدار. Git نفسه لا يفرض شيئاً من ذلك، لذلك إذا لم يُتفق عليه يعمل كل شخص بطريقته، وبعد شهر تجد عشرات الفروع في المستودع، ولا تعرف ما الذي نُشر، وتتحول عمليات الدمج إلى تعارضات تستغرق ساعات.
أشهر النماذج هي Git Flow وGitHub Flow (وهو صورة مبسطة من Feature Branch Workflow) وtrunk-based development. وليست هذه النماذج «قديماً وجديداً»، بل أدوات لظروف مختلفة: كم مرة تصدر إصدارات، وكم نسخة من المنتج تدعمها في آن واحد، ومدى قوة اختباراتك الآلية.
Git Flow: نموذج بفرعي develop وrelease
وصف فنسنت دريسن Git Flow عام 2010. يقوم النموذج على فرعين دائمين:
- main (في المشاريع القديمة master) — الكود المنشور فقط، وكل commit هنا يقابل إصداراً ويُوسم بعلامة (tag)؛
- develop — فرع التكامل الذي تُدمج فيه الميزات الجاهزة للإصدار التالي.
وثلاثة أنواع من الفروع المؤقتة:
| الفرع | يُنشأ من | يُدمج في | الغرض |
|---|---|---|---|
| feature/* | develop | develop | تطوير ميزة جديدة |
| release/* | develop | main وdevelop | تثبيت الإصدار: إصلاحات الأخطاء، ورقم الإصدار، والتوثيق |
| hotfix/* | main | main وdevelop | إصلاح عاجل في إصدار منشور |
Git Flow بأوامر Git الخالصة
دورة ميزة واحدة وإصدار واحد تبدو هكذا:
# ميزة
git switch develop
git switch -c feature/cart-discounts
# ... commits ...
git switch develop
git merge --no-ff feature/cart-discounts
git branch -d feature/cart-discounts
# إصدار
git switch -c release/1.4.0 develop
# ... تعديل رقم الإصدار وإصلاحات الأخطاء ...
git switch main
git merge --no-ff release/1.4.0
git tag -a v1.4.0 -m "Release 1.4.0"
git switch develop
git merge --no-ff release/1.4.0
git branch -d release/1.4.0
الخيار --no-ff أساسي في هذا النموذج: فهو ينشئ commit دمج حتى حيث يمكن التقدم السريع (fast-forward)، فيبقى ظاهراً في التاريخ أي الـ commits تنتمي إلى ميزة أو إصدار واحد.
امتداد git-flow
حتى لا تكتب هذه التسلسلات يدوياً، توجد أداة git-flow (ويُثبَّت عادةً الفرع المُصان منها، وفي مديري الحزم يُسمى غالباً git-flow-avh). تضيف الأداة أوامر فرعية:
git flow init -d
git flow feature start cart-discounts
git flow feature finish cart-discounts
git flow release start 1.4.0
git flow release finish 1.4.0
git flow hotfix start 1.4.1
git flow hotfix finish 1.4.1
يسأل git flow init عن أسماء الفروع والبادئات (ومع -d يقبل القيم الافتراضية)، وتنفذ أوامر finish كل عمليات الدمج اللازمة، وتضع الوسم، وتحذف الفرع المؤقت. وكثير من واجهات Git الرسومية تعمل وفق Git Flow عبر الأزرار أيضاً.
مزايا Git Flow وعيوبه
المزايا: بنية واضحة، وحالة مفهومة للكود (main هو ما لدى العملاء، وdevelop هو الإصدار القادم)، ومرحلة مستقلة لتثبيت الإصدار، ودعم مريح للإصلاحات العاجلة. وهو يناسب تطبيقات الجوال والبرمجيات المغلفة والمكتبات والبرامج الثابتة، أي حيث يمر الإصدار بمراجعة متجر التطبيقات أو يُثبَّت عند العميل.
العيوب: فروع كثيرة وعمليات دمج كثيرة، وفروع الميزات طويلة العمر تتراكم فيها الفروقات عن develop، وينسى الناس بسهولة الدمج المزدوج في release وhotfix. وبالنسبة لموقع ويب يُنشر عدة مرات في اليوم تصبح فروع الإصدار بيروقراطية بلا فائدة. ومؤلف النموذج نفسه أضاف عام 2020 ملاحظة على مقالته: للبرمجيات التي تُسلَّم باستمرار يُنظر في أساليب أبسط، مثل GitHub Flow.
GitHub Flow: فروع قصيرة وpull request
GitHub Flow نسخة مخففة من Feature Branch Workflow. وقواعده قليلة:
- يبقى main دائماً في حالة عمل وجاهزاً للنشر.
- يُنجز كل عمل في فرع من main باسم واضح.
- يُرفع الفرع بانتظام ويُفتح له pull request (أو merge request) للنقاش.
- بعد المراجعة ونجاح CI يُدمج الفرع في main.
- ما يُدمج في main يُنشر فوراً.
git switch main
git pull
git switch -c feature/new-payment
# ... commits برسائل واضحة ...
git push -u origin feature/new-payment
# نفتح pull request، ونجتاز المراجعة وCI، ثم ندمج في main
المزايا: فرع دائم واحد، وحد أدنى من القواعد، ومسار سريع من الفكرة إلى الإنتاج، ومراجعة كود طبيعية. العيوب: لا يوجد مكان مدمج لدعم عدة إصدارات أو لتثبيت الإصدار قبل نشره، ومن دون اختبارات آلية جيدة يبقى شعار «main دائماً جاهز» أمنية فقط.
نهج مشابه بفروع إضافية للبيئات (مثل staging وproduction) يُسمى أحياناً GitLab Flow. وهو مفيد عندما لا يكون النشر إلى الإنتاج فورياً، بل وفق جدول زمني أو بعد فحص يدوي.
Trunk-based development
في trunk-based development يدمج جميع المطورين تغييراتهم في فرع واحد (trunk، أي main) بشكل متكرر — مرة واحدة يومياً على الأقل. والفروع إن وُجدت تعيش ساعات لا أسابيع. والوظائف غير المكتملة تدخل main لكنها مخفية خلف feature flag:
if (featureFlags.isEnabled("new-checkout", user)) {
renderNewCheckout();
} else {
renderOldCheckout();
}
الميزة الكبرى شبه غياب التعارضات الكبيرة: التغييرات صغيرة وتُدمج وكل شخص ما زال يتذكر السياق. ويدعم النموذج التكامل المستمر بمعناه الحرفي، ويناسب الفرق الناضجة ذات CI السريع وتغطية الاختبارات العالية. وتُسمح فروع الإصدار، لكنها تُنشأ من trunk مباشرة قبل النشر، وتُدخل الإصلاحات أولاً في trunk ثم تُنقل عبر cherry-pick.
العيوب: يتطلب انضباطاً وبنية تحتية — اختبارات سريعة، وflags، ومراقبة. والمبتدئ يخاف من الـ commit يومياً في main، وCI ضعيف يحول trunk إلى فرع معطوب باستمرار. كما يجب حذف الـ flags في وقتها، وإلا تتراكم في الكود تفرّعات ميتة.
مقارنة Git Flow وGitHub Flow وtrunk-based
| المعيار | Git Flow | GitHub Flow | Trunk-based |
|---|---|---|---|
| الفروع الدائمة | main وdevelop | main | main (trunk) |
| عمر فرع الميزة | أيام وأسابيع | أيام | ساعات أو بلا فروع |
| تكرار الإصدارات | إصدارات مخططة | حسب الدمج | مستمر |
| دعم عدة إصدارات | نعم، عبر الوسوم وhotfix | غير مريح | عبر فروع release |
| متطلبات CI والاختبارات | متوسطة | عالية | عالية جداً |
| خطر التعارضات الكبيرة | مرتفع | متوسط | منخفض |
| حاجز الدخول | يجب تعلم القواعد | أدنى حد | يحتاج ثقافة وfeature flags |
| المشروع النموذجي | تطبيق جوال، مكتبة، برنامج مغلف | خدمة ويب، مفتوح المصدر، شركة ناشئة | منتج كبير بنشر متكرر |
كيف تختار سير العمل للفريق
أجب عن بعض الأسئلة:
- هل يجب دعم عدة إصدارات منشورة؟ إذا كان العملاء على الإصدارين 2.x و3.x ويحتاجان إلى إصلاحات — فـ Git Flow أو trunk-based مع فروع release.
- كم مرة تنشر؟ مرة كل ربع سنة — Git Flow لن يعيقك. كل يوم — اختر GitHub Flow أو trunk-based.
- ما مدى ثقتك في الاختبارات؟ إذا كان CI بطيئاً والتغطية ضعيفة، فابدأ بـ GitHub Flow مع مراجعة إلزامية، واقصّر الفروع تدريجياً.
- ما حجم الفريق وخبرته؟ الفريق الصغير والمبتدئون يجدون GitHub Flow الأسهل: قاعدة واحدة «main دائماً جاهز» وpull request لكل شيء.
نصيحة عملية: لا تختر Git Flow «لأن هذا هو المعتاد». ابدأ بأبسط عملية تحل مشكلاتك، وعقّدها فقط عندما تظهر مشكلة محددة — كأن تحتاج إلى مرحلة تثبيت أو دعم إصدارات قديمة.
قواعد تنجح مع أي سير عمل
- احمِ main على الخادم: امنع الإرسال المباشر، واطلب المراجعة وCI أخضر قبل الدمج.
- اتفق على أسماء الفروع:
feature/وfix/وhotfix/مع رقم المهمة. - أبقِ الفروع قصيرة: كلما طال عمر الفرع صار دمجه أصعب. حدّث فرعك من main بانتظام عبر merge أو rebase.
- اختر استراتيجية دمج pull request (merge commit أو squash أو rebase) واستخدمها بشكل موحد.
- ضع وسوماً للإصدارات بوسوم مُعلَّقة (annotated) وفق الترقيم الدلالي (semantic versioning).
الإصلاح العاجل في كل نموذج: خلل واحد وثلاث عمليات
الفرق بين النماذج يظهر أوضح ما يكون في إصلاح عاجل على الإنتاج.
في Git Flow يُنشأ hotfix/1.4.1 من main، وبعد الإصلاح يُدمج الفرع في main مع الوسم v1.4.1، وبالضرورة في develop (أو في فرع release الحالي إن وُجد). ونسيان الدمج الثاني خطأ كلاسيكي: في الإصدار التالي يعود الخلل «للظهور».
git switch -c hotfix/1.4.1 main
git commit -am "إصلاح حساب الخصم"
git switch main && git merge --no-ff hotfix/1.4.1
git tag -a v1.4.1 -m "Hotfix 1.4.1"
git switch develop && git merge --no-ff hotfix/1.4.1
في GitHub Flow يكون الإصلاح العاجل فرعاً قصيراً عادياً من main مع pull request يُمنح أولوية في المراجعة. وبعد الدمج يذهب إلى الإنتاج عبر النشر التلقائي نفسه، ولا توجد عملية منفصلة.
في trunk-based يُعمل الإصلاح أولاً على trunk، وإذا كان فرع release يعمل في الإنتاج يُنقل الـ commit إليه عبر git cherry-pick -x <hash>. ويضيف الخيار -x إلى الرسالة رابطاً للـ commit الأصلي، فتبقى الصلة ظاهرة في التاريخ. وإذا كانت المشكلة في ميزة جديدة فكثيراً ما يكفي إطفاء feature flag الخاص بها دون لمس الكود أصلاً.
كيف تجيب في المقابلة عن «أي سير عمل Git استخدمت»
المحاور لا يختبر معرفتك بالأسماء، بل فهمك للمفاضلات. الإجابة الجيدة: تسمي النموذج، وتصف كيف تُدار الفروع والمراجعة فيه، وتشرح لماذا كان مناسباً للمشروع. مثلاً: «استخدمنا GitHub Flow: فروع من main، وpull request مع مراجعة إلزامية وCI، وبعد الدمج نشر تلقائي. لم نأخذ Git Flow لأننا كنا ننشر عدة مرات في الأسبوع، وفروع الإصدار كانت ستبطئنا فقط». ومن الميزات أن تذكر ما كنت ستغيّره لو كبرت العملية.
الخلاصة
Git Flow نموذج صارم بفروع develop وrelease وhotfix، ومناسب للمنتجات ذات الإصدارات المخططة ودعم عدة إصدارات. وGitHub Flow أبسط ويناسب معظم مشاريع الويب، أما trunk-based development فيعطي أقصى سرعة لكنه يحتاج إلى CI ناضج وfeature flags. اختر سير العمل حسب تكرار الإصدارات وجودة الاختبارات، لا حسب الموضة. وتُشرح النماذج الثلاثة مع فرقها وأخطائها الشائعة عملياً في دورة Git.
الأسئلة الشائعة
هل أصبح Git Flow قديماً؟
لا، لكن مجال استخدامه تقلّص. لمواقع الويب ذات النشر المستمر يبدو مبالغاً فيه، أما لتطبيقات الجوال والمكتبات والبرمجيات المغلفة فما زال مناسباً.
هل نحتاج إلى فرع develop إذا كانت الإصدارات متكررة؟
غالباً لا: develop يكرر main ويضيف دمجاً إضافياً. مع الإصدارات المتكررة يكفي main وفروع الميزات القصيرة.
هل يمكن استخدام rebase في Git Flow؟
يمكن تحديث develop داخل فرع الميزة الخاص بك عبر rebase إذا كان غير منشور أو شخصياً، لكن الدمج النهائي في develop وmain في النموذج الكلاسيكي يتم بـ merge --no-ff للحفاظ على بنية التاريخ.
لماذا يُفضَّل feature flag على فرع ميزة طويل العمر؟
لأن الكود يُدمج في main فوراً ولا ينفصل عن بقية قاعدة الكود، وتصبح تفعيل الميزة قراراً منفصلاً يمكن التراجع عنه دون التراجع عن الكود.
كيف أنتقل من Git Flow إلى GitHub Flow؟
أنهِ الإصدارات الجارية، واندمج develop في main، وأعد ضبط CI لينشر من main، واحذف develop. وأنشئ الفروع الجديدة من main فقط.
يُشرح هذا الموضوع بالتفصيل مع التطبيق العملي في دورة «Git من الصفر إلى مطور واثق».
الانتقال إلى الدورة