#!/usr/bin/env bash
#
# Nothing uncommitted goes out with a push.
#
# ## Where this came from
#
# There was already a `pre-push` in `.git/hooks` on this checkout when the
# template was adopted on 2026-08-18. Setting `core.hooksPath` to this directory
# would have silently stopped it running — the whole class of failure these
# hooks exist to prevent, applied to a hook — so its useful half was moved here,
# where it is versioned and every clone gets it.
#
# ## What was kept, and what was dropped
#
# **Kept:** the two working-tree checks. A push that leaves edits behind is how
# documentation ends up one commit adrift of the code it describes, and
# `docs/WORK_CYCLE.md` is built on those travelling together.
#
# **Dropped:** its third check, which refused the push when the branch was ahead
# of its remote. That is the precondition for pushing at all, so it fired on
# every real push and its only instruction was to re-run with `--no-verify` — a
# guard that can never pass teaches people to bypass the two beside it that can.
#
# ## Escape hatch
#
#   git push --no-verify
#
# Deliberately not silent about it: the checks below print what they found
# before refusing, so an intentional bypass is a decision with the evidence in
# front of it.
#
# Exit 0 nothing outstanding, 1 something is.

set -uo pipefail

cd "$(git rev-parse --show-toplevel)" || exit 1

say() { printf '\033[1mpre-push:\033[0m %s\n' "$*" >&2; }

fail=0

if ! git diff --quiet --exit-code; then
  say "uncommitted working-tree changes:"
  git diff --name-only | sed 's/^/  /' >&2
  fail=1
fi

if ! git diff --cached --quiet --exit-code; then
  say "staged but uncommitted changes:"
  git diff --cached --name-only | sed 's/^/  /' >&2
  fail=1
fi

if [ "$fail" -ne 0 ]; then
  say ""
  say "push refused. Commit or stash the above first — a push that leaves edits"
  say "behind is how the documents end up describing a commit nobody is running."
  say "To push anyway: git push --no-verify"
  exit 1
fi

exit 0
