git-status 2.0

Den som är först på morgonen git status att skriva och sedan dricka kaffe kommer att älska den här lilla hjälparen. Speciellt när många webbprojekt utvecklas parallellt är ett värt det kompakt översikt: Var är det arbetande trädet rent, var hänger osammanfogade förändringar och var väntar en pull/push? Ett litet skalverktyg kommer att göra susen - så länge det robust hanterar utrymmen/Unicode i banor och inte kvävs av hängande fjärrkontroller.


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

Manuset behöver fortfarande... chmod +x ~/path/to/script.sh göra den körbar och kan skapa ett alias för att spara värdefull skrivning: Här lägger du till hans ~/.bashrc / ~/.zshrc / ~/.bash_profile inträdet alias gscan='bash /path/to/script.sh' dessutom. Från och med då, en enkel gscan i önskad rotkatalog.

Ett ord om varför den andra körningen är märkbart snabbare: Under den första körningen måste filsystemet fortfarande skanna igenom allt; efter det är metadata och många .git-Strukturer har landat i operativsystemets sidcache. Referenser och commit-grafer har också redan värmts upp. Nästa fetch Den överför nu mestadels bara små deltan. Ingen instrumentpanel, ingen overhead – en snabb statusbild direkt i terminalen.

Tillbaka