حالة git 2.0

من هو الأول في الصباح git status أي شخص يكتب ثم يشرب القهوة سيحب هذا المساعد الصغير. خاصةً عند تطوير العديد من مشاريع الويب بالتوازي، فإن نظرة عامة موجزة لا تُقدر بثمن: أين شجرة العمل نظيفة، وأين التغييرات غير المدمجة، وأين عمليات السحب/الدفع المعلقة؟ أداة شل صغيرة هي كل ما تحتاجه - طالما أنها تتعامل مع المسافات/Unicode في المسارات بكفاءة ولا تُعيق عمل وحدات التحكم عن بُعد العالقة.


#!/usr/bin/env bash

set -Eeuo pipefail
export LC_ALL=C

# Check if a command exists (no output).
have() { command -v "$1" >/dev/null 2>&1; }

# Ensure we have a `sort` that supports -z (NUL-delimited) input.
SORT_BIN="sort"
if ! "$SORT_BIN" -z </dev/null 2>/dev/null; then
  if have gsort && gsort -z </dev/null 2>/dev/null; then
    SORT_BIN="gsort"
  else
    printf 'Error: This script requires "sort -z" (GNU coreutils). Install coreutils (gsort).\n' >&2
    exit 1
  fi
fi

# Use GNU `timeout` if available; otherwise try `gtimeout` (macOS); otherwise no timeout.
TIMEOUT_BIN="timeout"
if ! have "$TIMEOUT_BIN"; then
  if have gtimeout; then
    TIMEOUT_BIN="gtimeout"
  else
    TIMEOUT_BIN=""
  fi
fi

# Require git.
if ! have git; then
  printf 'Error: "git" not found.\n' >&2
  exit 1
fi

# Remove a leading "./" from a path for cleaner output.
trim_dot_slash() {
  case "$1" in
    ./*) printf '%s\n' "${1#./}" ;;
    *)   printf '%s\n' "$1" ;;
  esac
}

# Legend + divider (as requested)
printf '\n🟢: clean\n🟡: behind/ahead\n🔴: modified\n\n----------------------------------\n\n'

# Find all .git directories, NUL-delimited; sort NUL-delimited; iterate safely.
find . -type d -name .git -print0 \
| "$SORT_BIN" -z \
| while IFS= read -r -d '' gitdir; do
    repo="${gitdir%/.git}"
    display_path="$(trim_dot_slash "$repo")"

    # Skip anything that isn't a proper work tree (safety check).
    if ! git -C "$repo" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
      continue
    fi

    # Working tree status; include untracked files for a strict "red" signal.
    status_out="$(git -C "$repo" status --porcelain=v1 || true)"

    # Upstream divergence check (only if an upstream is configured).
    ahead=0
    behind=0
    if git -C "$repo" rev-parse --abbrev-ref --symbolic-full-name '@{u}' >/dev/null 2>&1; then
      # Refresh refs; protect with timeout so a hanging remote doesn't stall the loop.
      if [ -n "$TIMEOUT_BIN" ]; then
        "$TIMEOUT_BIN" 10s git -C "$repo" fetch --all --prune >/dev/null 2>&1 || true
      else
        git -C "$repo" fetch --all --prune >/dev/null 2>&1 || true
      fi
      # Count commits only on our side (ahead) and only on upstream's side (behind).
      ahead="$(git  -C "$repo" rev-list --count --left-only  HEAD...@{u} 2>/dev/null || echo 0)"
      behind="$(git -C "$repo" rev-list --count --right-only HEAD...@{u} 2>/dev/null || echo 0)"
    fi

    # Decide the signal:
    # - RED if the working tree isn't clean
    # - YELLOW if clean but ahead/behind of upstream
    # - GREEN otherwise
    if [ -n "$status_out" ]; then
      printf '🔴 %s\n' "$display_path"
    else
      if [ "${ahead:-0}" -gt 0 ] || [ "${behind:-0}" -gt 0 ]; then
        printf '🟡 %s\n' "$display_path"
      else
        printf '🟢 %s\n' "$display_path"
      fi
    fi
  done

لا يزال النص بحاجة إلى... chmod +x ~/path/to/script.sh جعله قابلاً للتنفيذ ويمكنه إعداد اسم مستعار لحفظ الكتابة القيمة: هنا يمكنك الإضافة إلى ~/.bashrc / ~/.zshrc / ~/.bash_profile المدخل alias gscan='bash /path/to/script.sh' بالإضافة إلى ذلك. ومنذ ذلك الحين، أصبح الأمر بسيطًا gscan في الدليل الجذر المطلوب.

أحد الأسباب التي تجعل التشغيل الثاني أسرع بشكل ملحوظ: أثناء التشغيل الأول، لا يزال يتعين على نظام الملفات مسح كل شيء؛ بعد ذلك، تتم معالجة البيانات الوصفية والعديد من الأشياء الأخرى بالفعل. .gitتم وضع الهياكل في ذاكرة التخزين المؤقت لصفحات نواة نظام التشغيل، وتم بالفعل إعداد المراجع ورسومات الالتزام. الخطوة التالية... fetch الآن، لا ينقل النظام سوى بيانات صغيرة في الغالب. لا لوحة معلومات، ولا تكاليف إضافية - لقطة سريعة للحالة مباشرةً في الجهاز.

عودة