starea git 2.0

Oricine este primul dimineața git status tastând și apoi beți cafea îl va iubi pe acest mic ajutor. Mai ales când multe proiecte web sunt dezvoltate în paralel, unul merită imagine de ansamblu compactă: Unde este arborele de lucru curat, unde sunt agățate modificările necontopite și unde așteaptă o tragere/împingere? Un instrument shell mic va face truc - atâta timp cât gestionează robust spațiile/Unicode în căi și nu se sufocă cu telecomenzile suspendate.


#!/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

Scenariul trebuie încă... chmod +x ~/path/to/script.sh îl face executabil și poate configura un alias pentru a economisi tastare valoroasă: Aici adăugați la al său ~/.bashrc / ~/.zshrc / ~/.bash_profile intrarea alias gscan='bash /path/to/script.sh' în plus. De atunci încolo, un simplu gscan în directorul rădăcină dorit.

Câteva cuvinte despre motivul pentru care a doua rulare este vizibil mai rapidă: în timpul primei rulări, sistemul de fișiere încă trebuie să scaneze totul; după aceea sunt metadate și multe .git-Structurile au ajuns în memoria cache a paginilor sistemului de operare. Referințele și graficele de commit au fost deja încălzite. Următorul fetch Acum transmite în mare parte doar mici diferențe. Fără tablou de bord, fără costuri suplimentare - o instantanee rapidă a stării direct în terminal.

Înapoi