scripts/git-hooks/pre-push refuses a delete, a remote tip missing locally and
any update whose remote tip isn't an ancestor of the local tip; a new branch
and a fast-forward pass. scripts/install-git-hooks.sh puts it in the common
.git/hooks, refuses a set core.hooksPath or a different hook without
--force-install, and runs a canary through git hook run. scripts/slot-busy.sh
exits 0 CLEAR, 1 BUSY, 2 error. scripts/test-git-guard.sh (61 cases) and the
mutant runner in agents/darkwing/work/queue-211; TOOLS.md gets the usage.
Not run yet: written during Gander's slot (sage-2801).
(cherry picked from commit 20e885dc35)
51 lines
2.0 KiB
Bash
Executable File
51 lines
2.0 KiB
Bash
Executable File
#!/bin/sh
|
|
# Mosaic push guard (row 211, #426). Installed as pre-push in the common
|
|
# .git/hooks by `scripts/install-git-hooks.sh`; never edit the installed copy.
|
|
# For each ref git is about to push, it refuses a delete and any update whose
|
|
# remote tip isn't an ancestor of the local tip: a force-push or a +refspec.
|
|
# A new ref passes. It fetches nothing, so a remote tip that isn't in this
|
|
# repository refuses. Any failure of the check refuses too.
|
|
# `git push --no-verify` skips this hook; Gitea's branch protection is the
|
|
# guard that holds (ops-01). Canon: never force-push.
|
|
refused=0
|
|
refuse() {
|
|
echo "mosaic push guard: refused: $*" >&2
|
|
refused=1
|
|
}
|
|
zero() {
|
|
case "$1" in *[!0]*) return 1 ;; esac
|
|
return 0
|
|
}
|
|
while read -r lref lsha rref rsha extra || [ -n "${lref:-}" ]; do
|
|
if [ -z "${rsha:-}" ] || [ -n "${extra:-}" ]; then
|
|
refuse "unreadable ref line from git: $lref ${lsha:-} ${rref:-} ${rsha:-} ${extra:-}"
|
|
continue
|
|
fi
|
|
case "$lsha$rsha" in
|
|
*[!0-9a-f]*) refuse "$rref: a ref line with a non-hex object name (local $lsha, remote $rsha)"; continue ;;
|
|
esac
|
|
if zero "$lsha"; then
|
|
refuse "$rref: a delete (remote $rsha); seat branches are never deleted by push"
|
|
continue
|
|
fi
|
|
if zero "$rsha"; then
|
|
continue
|
|
fi
|
|
if ! git cat-file -e "$rsha^{commit}" 2>/dev/null; then
|
|
refuse "$rref: the remote tip $rsha isn't in this repository; fetch first, then push again"
|
|
continue
|
|
fi
|
|
git merge-base --is-ancestor "$rsha" "$lsha" 2>/dev/null
|
|
rc=$?
|
|
case $rc in
|
|
0) ;;
|
|
1) refuse "$rref: not a fast-forward; remote $rsha isn't an ancestor of local $lsha (a force-push or a rewritten branch)" ;;
|
|
*) refuse "$rref: the ancestor check failed (exit $rc) for remote $rsha and local $lsha" ;;
|
|
esac
|
|
done
|
|
if [ "$refused" != 0 ]; then
|
|
echo "mosaic push guard: nothing was pushed; a pushed branch only moves forward (canon: never force-push)" >&2
|
|
exit 1
|
|
fi
|
|
exit 0
|