Όποιος είναι πρώτος το πρωί git status πληκτρολογώντας και μετά πίνοντας καφέ θα λατρέψει αυτόν τον μικρό βοηθό. Ειδικά όταν πολλά web projects αναπτύσσονται παράλληλα, ένα αξίζει τον κόπο συμπαγής επισκόπηση: Πού είναι καθαρό το δέντρο εργασίας, πού κρέμονται οι μη συγχωνευμένες αλλαγές και πού περιμένει ένα τράβηγμα/ώθηση; Ένα μικρό εργαλείο κελύφους θα κάνει το κόλπο - αρκεί να χειρίζεται στιβαρά κενά/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 Πλέον, μεταδίδει κυρίως μόνο μικρά δέλτα. Χωρίς πίνακα ελέγχου, χωρίς επιβάρυνση – ένα γρήγορο στιγμιότυπο κατάστασης απευθείας στο τερματικό.