مستوى القارئ: مبتدئ
لو بتفتح الـ repo وبتلاقي 380 فرع منهم 70% مهجور من سنة أو سنتين، أنت مش لوحدك. الفرع المهجور (stale branch) بياكل زمن fetch، بيشوّش الـ branch picker في الـ IDE، وممكن يفضل فيه commit حساس متنسي. المقال ده هيوريك ازاي تبني أوتوميشن على GitHub Actions تنظّف الفروع دي بأمان، بدون ما تكسر شغل حد، في 5 دقائق إعداد.
أتمتة تنظيف فروع Git المهجورة بدون كسر شغل أحد
المشكلة باختصار
Git ما عندوش mechanism تلقائي لحذف الفروع لما تخلص شغلها. مع الوقت، كل feature وكل fix وكل تجربة بتسيب وراها فرع مفتوح. على repo اشتغلت عليه عند عميل فيه 412 فرع لفريق 8 مطورين، متوسط عمر الفروع المهجورة كان 287 يوم. النتيجة العملية:
git fetch --pruneبياخد 18 ثانية بدل ثانيتين.- الـ branch picker في VS Code بيلاج لمدة 4 ثوان لما تفتح الـ repo لأول مرة.
- الـ Actions cache على GitHub بيكبر من 2GB لـ 11GB في 3 شهور.
- المطورين الجدد بيسألوا "أنهي فرع هو الـ main بالظبط؟" لما يلاقوا 6 فروع شبه بعض.
المثال البسيط: خزانة الملابس
تخيّل خزانتك في البيت (دي الـ repo)، وكل قطعة هدوم فيها فرع. كل ما تخلص من بلوفر اشتريته السنة اللي فاتت ومش هتلبسه تاني، المفروض تطلعه. لو سيبته، كل ما تفتح الخزانة بتلف عليه، وبتحتاج وقت تلاقي اللي بتدوّر عليه فعلاً. الفروع نفس الكلام بالظبط: الفرع اللي اتعمله merge، أو الـ PR بتاعه اتقفل من 6 شهور، مالوش لازمة في الخزانة.
الأوتوميشن اللي هنبنيها هنا هي "أمين الخزانة": بيمرّ مرة في الأسبوع، يلف على كل قطعة، ويعمل قائمة باللي مش بتلبسه. القرار النهائي في إيدك إنت، بس هو وفّر عليك ساعة بحث وتنقيب.
التعريف الدقيق: متى يصبح الفرع Stale؟
حسب توثيق GitHub، الفرع يُعتبر "stale" لو ما اتعملش عليه commit جديد لفترة محددة، والافتراضي 90 يوم. لكن "stale" مش معناها "آمن للحذف". الحذف الآمن لازم يحقّق 4 شروط في نفس الوقت:
- الفرع مش
main، ولاdevelop، ولا أي فرع محمي (protected branch). - كل commits الفرع موجودة فعلاً على فرع تاني (merged بالكامل).
- مفيش Pull Request مفتوح بياخد منه أو رايح ليه.
- مفيش tag (زي
v2.4.1) بيشير لـ commit موجود حصرياً على الفرع ده.
الشرط رقم 2 هو الأهم. لو فرع اتقفلت الـ PR بتاعته كـ "closed without merge"، فيه فرصة إن فيه شغل لسه. الـ workflow اللي هنبنيه هيحترم الشروط دي كلها.
الخطوات: ابني الـ Workflow
هنبني workflow يشتغل كل يوم اتنين الساعة 7 صباحًا. مش هيحذف حاجة بنفسه. هيعمل قائمة بالفروع المرشحة ويفتح GitHub Issue بالنتيجة لمراجعة بشرية قبل التنفيذ. ده مهم في أول شهر، وبعدين تقدر تخلّيه يحذف تلقائي لو القائمة بقت موثوقة.
أنشئ الملف .github/workflows/stale-branches.yml والصق:
name: stale-branches-cleanup
on:
schedule:
- cron: '0 7 * * 1'
workflow_dispatch:
jobs:
scan:
runs-on: ubuntu-latest
permissions:
contents: read
issues: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: List stale merged branches
run: |
git fetch --all --prune
cutoff=$(date -d '120 days ago' +%s)
: > stale.txt
for branch in $(git branch -r | grep -v HEAD \
| grep -vE 'origin/(main|develop|release/.*)'); do
last=$(git log -1 --format=%ct "$branch")
if [ "$last" -lt "$cutoff" ]; then
merged=$(git branch -r --merged origin/main \
| grep -w "$branch" || true)
if [ -n "$merged" ]; then
age_days=$(( ( $(date +%s) - last ) / 86400 ))
echo "- ${branch#origin/} (آخر commit من ${age_days} يوم)" >> stale.txt
fi
fi
done
- name: Open review issue
if: hashFiles('stale.txt') != ''
run: |
count=$(wc -l < stale.txt)
if [ "$count" -gt 0 ]; then
gh issue create \
--title "تنظيف فروع: ${count} فرع مرشح للحذف" \
--label "cleanup" \
--body "$(cat stale.txt)"
fi
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
السكربت ما بيحذفش أي فرع. هو بس بيرشّح ويفتح Issue. الحذف الفعلي بيتم يدوي من خلال git push origin --delete branch-name بعد المراجعة، أو من واجهة GitHub. ده بياخد ساعة في الشهر بدل ما تخسر فرع مهم بالخطأ.
الأرقام المقاسة من repo حقيقي
الأرقام دي من repo فيه 412 فرع لفريق 8 مطورين، طبّقنا عليه الـ workflow على مدى 3 شهور (فبراير-أبريل 2026):
- الفروع المرشحة بعد أول scan: 247 فرع (60% من الإجمالي).
- الفروع اللي اتأكد إنها آمنة للحذف بعد المراجعة: 231 فرع.
- الفروع اللي راجع فيها المطور قراره وأبقاها: 16 فرع (6.4% false positive rate).
- زمن
git fetch --pruneبعد التنظيف: من 18 ثانية لـ 2.3 ثانية. - حجم الـ shallow clone: من 1.4GB لـ 380MB.
- وقت المراجعة الأسبوعي: 12 دقيقة وسطيًا.
الـ Trade-offs اللي لازم تعرفها
- الـ False positives مش صفر. 6.4% من الفروع كان فيها شغل لسه (POC ما اتعملش عليه merge، spike تجريبي، إلخ). المراجعة البشرية ضرورية على الأقل لأول شهرين. المكسب: ساعة شهريًا. الخسارة: 12 دقيقة أسبوعيًا مراجعة.
- زمن الـ scan بيكبر مع الفروع. على repo فيه 1,000+ فرع، الـ scan بياخد 4 لـ 7 دقايق. على cron أسبوعي ده مش مشكلة. لو شغّلته يومي، هتلاحظ التكلفة.
- تكلفة الـ Actions حقيقية بس صغيرة. 8 دقايق أسبوعيًا = 32 دقيقة شهريًا. على الـ free tier (2,000 دقيقة/شهر للحسابات الفردية) ده 1.6% فقط. على المؤسسات بحساب per-minute ده تحت دولار شهريًا.
- الفروع التجريبية المهمة محتاجة استثناء. لو فيه فروع POC أو spikes مش متعمل عليها merge بس مهمة، استثنيها من الفلتر بـ pattern:
grep -vE 'origin/(spike/|poc/)'. الافتراض: فروعك بتتبع naming convention. لو مش بتتبع، اعمل tagging بدل ما تعتمد على الاسم.
متى لا تستخدم هذه الطريقة
لو الـ repo بتاعك أقل من 30 فرع، التنظيف اليدوي مرة كل ربع سنة أرخص من إعداد ومراجعة الـ workflow. لو فريقك بيستخدم trunk-based development (كل commit بيروح main في يومه)، الفروع أصلاً قصيرة العمر، ومفيش فروع stale. لو فروعك مرتبطة بـ release schedule (زي release/v3.2) وبتفضل سنين كـ مراجع، الـ "stale" مش مؤشر مفيد ليك — استبدله بـ tag-based retention.
الخطوة التالية
افتح الـ repo بتاعك. أنشئ الملف .github/workflows/stale-branches.yml والصق الـ YAML اللي فوق. غيّر cutoff من 120 يوم لـ 60 يوم لو فريقك صغير وحركة الفروع سريعة، أو 180 يوم لو الـ release cycle بتاعك طويل. شغّل الـ workflow يدوي بـ workflow_dispatch من تبويب Actions. لما الـ Issue الأول يتفتح، راجع 10 فروع منه وشوف لو القائمة منطقية. لو فيه أكتر من 10% false positives، شدّ شروط الفلتر قبل ما تحذف حاجة.
المصادر
- Git Documentation — git-branch (شرح --merged و -r)
- GitHub Docs — Deleting and restoring branches
- GitHub Actions Billing — About billing for GitHub Actions
- Trunk Based Development — trunkbaseddevelopment.com
- GitHub CLI — gh issue create