#!/usr/bin/env sh
# Pre-push: gate the tree of every ref being pushed. NEVER bypass with
# --no-verify. Local scope stays cheap; CI does the full pass. What runs here,
# and what a receipt may skip, is the gate matrix in rules/23.
#
# The checks must judge what is BEING PUSHED, not what happens to be lying
# around in the working tree, so uncommitted work is parked first and restored
# by the trap — even on failure or interrupt.

STASHED=0
STASH_REF=""
STASH_MSG="pre-push-verify-$$"
if ! git diff --quiet HEAD || [ -n "$(git ls-files --others --exclude-standard)" ]; then
  STASH_BEFORE=$(git rev-parse -q --verify refs/stash 2>/dev/null || true)
  if git stash push --quiet --include-untracked --message "$STASH_MSG"; then
    STASH_AFTER=$(git rev-parse -q --verify refs/stash 2>/dev/null || true)
    if [ -n "$STASH_AFTER" ] && [ "$STASH_AFTER" != "$STASH_BEFORE" ]; then
      STASHED=1
      STASH_REF=$STASH_AFTER
      echo "pre-push: parked uncommitted work so the checks see exactly what is being pushed"
    fi
  fi
fi

restore_stash() {
  if [ "$STASHED" = "1" ]; then
    STASHED=0
    sh scripts/hooks/restore-stash.sh "$STASH_REF" "$STASH_MSG"
  fi
}
trap 'restore_stash' EXIT INT TERM

# git writes `<local ref> <local sha> <remote ref> <remote sha>` per pushed ref
# to this hook's stdin. pre-push.mjs reads ALL of it (an unread pipe can make
# git die with EPIPE after a green run) and validates each pushed ref's tree.
if ! node tools/gates/pre-push.mjs; then
  echo "→ pre-push failed"
  restore_stash
  exit 1
fi

restore_stash
trap - EXIT INT TERM

echo "pre-push OK"
