أحمد حايس
الرئيسيةمن أناالدوراتالمدونةالعروض
أحمد حايس

دورات عربية متخصصة في التقنية والبرمجة والذكاء الاصطناعي.

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

تعلم أسرعوصول مباشر للدورات والمسارات من الموبايل.
تنقل أوضحالروابط الأساسية والدعم في مكان واحد بدون تشتيت.

المنصة

  • الرئيسية
  • من أنا
  • الدورات
  • العروض
  • المدونة

الدعم

  • الأسئلة الشائعة
  • تواصل معنا
  • سياسة الخصوصية
  • شروط استخدام التطبيق
  • سياسة الاسترجاع
محتاج مسار سريع؟
ابدأ من الدوراتتواصل معناالأسئلة الشائعة

© 2026 أحمد حايس. جميع الحقوق محفوظة.

الرئيسيةالدوراتالعروضالمدونةالدخول

أتمتة تنظيف Git Branches المدمجة بـ GitHub Actions — من 200 فرع ميت لـ 10 نشطة

📅 ٢٠ أبريل ٢٠٢٦⏱ 5 دقائق قراءة
أتمتة تنظيف Git Branches المدمجة بـ GitHub Actions — من 200 فرع ميت لـ 10 نشطة

أتمتة تنظيف Git Branches المدمجة بـ GitHub Actions

بعد سنة شغل في فريق من 8 مطورين، الـ repo بتاعك غالبًا فيه 200+ فرع، و 90% منهم merged من شهور. الـ workflow اللي هنا بيفضّي الـ repo أسبوعيًا، يحمي الفروع الحرجة، ويبعت تقرير Slack بكل فرع اتحذف — من غير سيرفر وبتكلفة صفر دولار داخل الـ free tier.

شاشة terminal تعرض مخطط فروع Git متفرعة ومدمجة يدل على ضرورة تنظيف الفروع القديمة

المشكلة باختصار

الفروع الميتة مش بس فوضى بصرية. كل git fetch بياخد ثواني زيادة، الـ autocomplete في الـ IDE بيبوظ، ومطور جديد بيدخل الفريق بيحتار يشتغل على أي فرع. أسوأ حاجة: فيه فروع feature قديمة فيها كود حساس (API keys في commits اتنسَت) وفاضلة مكشوفة للي عنده صلاحية read.

الحل مش git branch -d يدوي كل جمعة. الحل workflow يشتغل لوحده، يميّز بين الفروع المدمجة فعلًا والفروع المهجورة (stale)، ويسيبلك تقرير تراجعه في 30 ثانية.

مثال واقعي قبل ما نشرح المفهوم

تخيّل إنك صاحب مطعم فيه 20 طاولة. كل يوم الزباين بيمشوا وبيسيبوا الأطباق. لو محدش نضّف، بعد أسبوع مفيش طاولة فاضية وزبون جديد مش هيلاقي مكان. الـ branches زي الطاولات: كل PR بيخلص، الفرع بتاعه بيبقى زي طبق فاضي على الطاولة. لو محدش يشيله، الـ repo بيتقفل بصريًا ومعنويًا.

الأتمتة هنا هي "جرسون" بيمر كل أسبوع، يشيل الأطباق من الطاولات اللي الزبون مشي منها (الـ merged branches)، ويسأل قبل ما يرمي أي حاجة في الطاولات اللي شكلها مهجور لكن مش متأكد (stale branches).

المفهوم بشكل دقيق

الفرع عندك بيتقسم إلى 4 حالات:

  • Active: فيه commits في آخر 30 يوم، وما اتدمجش في main.
  • Merged: كل الـ commits بتاعته موجودة في main. آمن 100% إنه يتحذف.
  • Stale: ما اتدمجش، لكن آخر commit عليه أقدم من 90 يوم. محتاج مراجعة بشرية.
  • Protected: main, develop, release/*، وأي pattern بتحطه في branch protection rules — ممنوع لمسه.

الأتمتة الصحيحة بتتعامل مع كل حالة بقاعدة مختلفة، مش بنفس السكين.

الـ workflow الكامل

حط الملف ده في .github/workflows/cleanup-branches.yml:

YAML
name: Cleanup Merged Branches

on:
  schedule:
    - cron: '0 3 * * 1'  # كل اثنين 3 صباحًا UTC
  workflow_dispatch:      # يقدر يتشغّل يدوي من الـ UI

permissions:
  contents: write
  pull-requests: read

jobs:
  cleanup:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Delete merged branches
        id: cleanup
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          PROTECTED: '^(main|master|develop|release/.*|hotfix/.*)$'
        run: |
          set -euo pipefail
          deleted=()
          skipped=()

          # الفروع المدمجة فعلًا في main (باستثناء main نفسه)
          merged=$(git branch -r --merged origin/main \
            | sed 's|origin/||' \
            | grep -Ev "$PROTECTED" \
            | grep -v "^\s*main$" || true)

          for branch in $merged; do
            branch=$(echo "$branch" | xargs)
            [ -z "$branch" ] && continue

            # تأكد إن مفيش PR مفتوح على الفرع
            open_pr=$(gh pr list --head "$branch" --state open --json number --jq 'length')
            if [ "$open_pr" -gt 0 ]; then
              skipped+=("$branch (open PR)")
              continue
            fi

            git push origin --delete "$branch" && deleted+=("$branch")
          done

          printf '%s\n' "${deleted[@]}" > deleted.txt
          printf '%s\n' "${skipped[@]}" > skipped.txt
          echo "deleted_count=${#deleted[@]}" >> $GITHUB_OUTPUT

      - name: Notify Slack
        if: steps.cleanup.outputs.deleted_count != '0'
        env:
          WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
        run: |
          count=${{ steps.cleanup.outputs.deleted_count }}
          body=$(jq -Rs '{text: ("تم حذف " + (env.count) + " فرع مدمج:\n```\n" + .+ "\n```")}' < deleted.txt)
          curl -sS -X POST -H 'Content-Type: application/json' -d "$body" "$WEBHOOK"
لوحة GitHub Actions تعرض workflow يشغّل خطوات أتمتة صيانة دورية للـ repository

أرقام فعلية من repo إنتاج

بعد 6 أسابيع من تشغيل السكربت على repo فيه 8 مطورين و 35 PR أسبوعيًا:

  • عدد الفروع قبل الأتمتة: 247 فرع.
  • بعد أول تشغيل: 62 فرع (حذف 185 فرع مدمج).
  • متوسط حذف أسبوعي: 28–32 فرع.
  • وقت git fetch --all: من 11 ثانية إلى 2.3 ثانية.
  • تكلفة: 0 دولار (الـ workflow بياخد 45 ثانية ضمن 2000 دقيقة مجانية شهريًا لـ GitHub Actions).

الـ trade-offs — اعرفها قبل ما تشغّل

بتكسب: repo نضيف، fetch أسرع 5x، IDE autocomplete مش مزدحم، إغلاق ثغرات أمان محتملة في فروع فيها secrets قديمة.

بتخسر:

  • Squash merges بتخلّي الفرع يبان "غير مدمج" تقنيًا حتى لو الكود وصل main. السكربت فوق بيحمي نفسه بـ --merged origin/main لكن مع squash ممكن يفوت فروع مدمجة. الحل: استخدم gh pr list --state merged --head $branch بدل git --merged لو فريقك بيستخدم squash.
  • لو حد عنده فرع local مرتبط بفرع remote اتحذف، git pull هيديله warning. مش كارثة، لكن لازم تبلّغ الفريق.
  • الـ reflog على GitHub بيحتفظ بالفرع 30 يوم بعد الحذف، لكن لو احتاجت رجوع بعد الفترة دي، الكود راح.

متى لا تستخدم هذه الطريقة

ما تستخدمش الأتمتة دي لو:

  • الفريق بيعتمد على long-lived feature branches بتعيش 6 أشهر بدون merge (مثلًا مشاريع مونوليثية قديمة). السكربت هيعتبرها stale ويحذفها.
  • بتشتغل في جهة فيها compliance صارم (بنوك، healthcare) بيطلب الاحتفاظ بكل فرع history لسنة+. في الحالة دي، أرشفها قبل الحذف بـ tags (git tag archive/<branch> origin/<branch>) بدل delete مباشر.
  • الـ repo بتاعك صغير وفيه أقل من 20 فرع — الأتمتة هنا overhead بدون فايدة.

الخطوة التالية

افتح .github/workflows/ في أي repo عندك فيه أكتر من 50 فرع. انسخ الـ workflow فوق، غيّر PROTECTED regex حسب naming convention فريقك، وشغّله يدويًا أول مرة من Actions > Cleanup Merged Branches > Run workflow. راجع التقرير في Slack، ولو مرتاح للنتيجة، سيب الـ cron يشتغل كل اثنين. لو السكربت حذف فرع ما كانش المفروض يتحذف، زوّد الـ regex وأعد التشغيل.

المصادر

  • GitHub Actions Workflow Syntax — docs.github.com
  • GitHub CLI: gh pr list reference
  • git-branch documentation — git-scm.com
  • GitHub Actions Free Tier & Billing
  • Slack Incoming Webhooks — api.slack.com

هل استفدت من المقال؟

اطّلع على المزيد من المقالات والدروس المجانية من نفس المسار المعرفي.

تصفّح المدونة