set -euo pipefail
[ -n "${GIT_HOOKS_SKIP:-}" ] && exit 0
echo "🚀 Running pre-push checks..."
BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null || echo "")
PUSH_REFS=""
if [ ! -t 0 ]; then
PUSH_REFS=$(cat)
fi
case "$BRANCH" in
main|master)
if [ -n "$PUSH_REFS" ]; then
while read -r _local_ref local_sha _remote_ref remote_sha; do
[ -z "$local_sha" ] && continue
if [ "$remote_sha" != "0000000000000000000000000000000000000000" ] && \
[ "$local_sha" != "0000000000000000000000000000000000000000" ]; then
if ! git merge-base --is-ancestor "$remote_sha" "$local_sha" 2>/dev/null; then
echo "❌ Force push to $BRANCH is not allowed."
echo " Override with: git push --no-verify"
exit 1
fi
fi
done <<EOF
$PUSH_REFS
EOF
fi
;;
esac
ALLOWED_PATTERN="^(feat|fix|docs|chore|refactor|test|ci|perf|release)/.*$"
SKIP_BRANCHES="^(main|master|dev|develop|staging|production|changeset-release/.*)$"
if [ -n "$BRANCH" ]; then
if ! grep -qE "$SKIP_BRANCHES" <<<"$BRANCH"; then
if ! grep -qE "$ALLOWED_PATTERN" <<<"$BRANCH"; then
echo "❌ Branch '$BRANCH' does not follow naming convention."
echo " Expected: type/description (e.g. feat/add-login)"
echo " Allowed prefixes: feat, fix, docs, chore, refactor, test, ci, perf, release"
echo " Override with: git push --no-verify"
exit 1
fi
fi
fi
LOCAL_HOOK="$(git rev-parse --show-toplevel)/.git-hooks/local-pre-push"
if [ -x "$LOCAL_HOOK" ]; then
"$LOCAL_HOOK" "$@" <<EOF
$PUSH_REFS
EOF
fi
echo "✅ Pre-push checks passed"