Closer/scripts/git-hooks/pre-push

62 lines
2.6 KiB
Bash
Executable File

#!/bin/sh
#
# Pre-push hook: catch stray uncommitted work and doomed non-fast-forward pushes
# BEFORE contacting the remote, with clearer messages than git's own errors.
#
# History: an earlier version of this hook also *blocked* whenever local commits
# existed that weren't on origin (an "unpushed commits" check). That is
# self-defeating — having commits to push is what a push IS — so it rejected
# every meaningful push and forced --no-verify. Removed 2026-07-11. The useful
# intent behind it (spot surprise commits from other sessions/tools sharing this
# branch) is now served by the informational listing below, which never blocks.
#
set -e
echo "--- pre-push: checking for uncommitted changes ---"
if ! git diff --quiet --exit-code; then
echo "⚠️ WARNING: You have uncommitted working-tree changes."
echo " These may be VS Code auto-saves or edits you forgot to stage."
echo " Run 'git diff' to review, then commit or stash before pushing."
echo ""
echo " To push anyway, run: git push --no-verify"
exit 1
fi
if ! git diff --cached --quiet --exit-code; then
echo "⚠️ WARNING: You have staged but uncommitted changes."
echo " These may be VS Code auto-stages or partial commits."
echo " Run 'git diff --cached' to review, then commit before pushing."
echo ""
echo " To push anyway, run: git push --no-verify"
exit 1
fi
BRANCH=$(git rev-parse --abbrev-ref HEAD)
REMOTE="origin/$BRANCH"
if git rev-parse --verify "$REMOTE" >/dev/null 2>&1; then
# Fail fast (with clear advice) if the remote-tracking ref already has commits
# we lack — the push would be rejected as non-fast-forward anyway. This catches
# the "another session pushed first" race with a better message than git's.
# (Based on the last fetch; a race newer than that is still caught by git itself.)
BEHIND=$(git rev-list --count "$BRANCH..$REMOTE" 2>/dev/null || echo 0)
if [ "$BEHIND" -gt 0 ]; then
echo "⚠️ BLOCKED: $REMOTE has $BEHIND commit(s) you don't have — this push would be rejected."
echo " Another session/tool likely pushed first (as of your last fetch)."
echo " Run: git pull --rebase then push again."
exit 1
fi
# Informational only — never blocks: show exactly what this push will publish,
# so commits made by other sessions/tools on this branch get noticed.
AHEAD=$(git rev-list --count "$REMOTE..$BRANCH" 2>/dev/null || echo 0)
if [ "$AHEAD" -gt 0 ]; then
echo "--- pre-push: publishing $AHEAD commit(s): ---"
git log --oneline "$REMOTE..$BRANCH" | sed 's/^/ /'
fi
fi
echo "--- pre-push: all checks passed ---"