# shellcheck shell=bash # Shared cherry-pick reject guard. SOURCED (never executed directly) by both # .githooks/prepare-commit-msg and .githooks/commit-msg. Git only executes files whose # names exactly match a hook name, so this underscore-prefixed helper is ignored by git. # # Recovers the upstream SHA of an in-progress cherry-pick and HARD-BLOCKS (returns 1) when # the triage ledger marks it "Won't merge". No-op for anything that is not a cherry-pick. # Fail-safe: only a confirmed, non-overridden reject returns 1; every other path returns 0. # # Override an intentional re-pick with either: # SH_CHERRYPICK_ALLOW_REJECT=1 git cherry-pick -x # git config sh.cherrypick.blockRejects false # warn-only for this repo sh_cherrypick_reject_guard() { local msgfile="${1:-}" local gd up_sha root triage py reason rc gd="$(git rev-parse --absolute-git-dir 2>/dev/null)" || return 0 up_sha="" if [ -f "$gd/CHERRY_PICK_HEAD" ]; then up_sha="$(tr -d '[:space:]' <"$gd/CHERRY_PICK_HEAD" 2>/dev/null)" fi if [ -z "$up_sha" ] && [ -n "$msgfile" ] && [ -f "$msgfile" ]; then up_sha="$(sed -n 's/.*cherry picked from commit \([0-9a-f]\{7,40\}\).*/\1/p' "$msgfile" | tail -1)" fi [ -z "$up_sha" ] && return 0 # not a cherry-pick -> no-op (normal commits untouched) root="$(git rev-parse --show-toplevel 2>/dev/null)" || return 0 triage="$root/scripts/triage.py" [ -f "$triage" ] || return 0 py="$(command -v python3 || command -v python 2>/dev/null)" [ -n "$py" ] || return 0 reason="$("$py" "$triage" check-reject "$up_sha" 2>/dev/null)" rc=$? [ "$rc" -eq 3 ] || return 0 # rc 0 = ok; rc 2 = ledger error -> fail-safe; only rc 3 blocks echo "" >&2 echo "sh-cherrypick: BLOCKED — ${up_sha:0:12} is marked \"Won't merge\" in the triage ledger." >&2 echo " reason: ${reason:-}" >&2 if [ "${SH_CHERRYPICK_ALLOW_REJECT:-}" = "1" ]; then echo " override: SH_CHERRYPICK_ALLOW_REJECT=1 — allowing this pick." >&2 return 0 fi if [ "$(git config --bool sh.cherrypick.blockRejects 2>/dev/null || echo true)" = "false" ]; then echo " override: sh.cherrypick.blockRejects=false — warn-only, allowing." >&2 return 0 fi echo " To re-evaluate intentionally: SH_CHERRYPICK_ALLOW_REJECT=1 git cherry-pick -x " >&2 echo " or repo-wide warn-only: git config sh.cherrypick.blockRejects false" >&2 echo " Recover this pick: git cherry-pick --abort (or --skip)" >&2 return 1 }