git-trace

romele / git-trace public

62 commits · 1 branch · 1 tag · 1 contributor (top: Romele) · first commit 3d ago · last push 17m ago

hooks/update

#!/bin/sh
# git-trace update hook — per-ref policy enforcement.
# Installed as <repo>.git/hooks/update by `git-trace mkrepo`.
# Args: refname oldrev newrev.  Env: REMOTE_USER is set when the push
# arrives over HTTP/SSH (empty for local file pushes).
ref="$1"
old="$2"
new="$3"

case "$ref" in
    refs/heads/*|refs/tags/*) ;;
    *)
        echo "git-trace: refusing to update '$ref'" >&2
        exit 1
        ;;
esac

zero=0000000000000000000000000000000000000000
def=$(git symbolic-ref -q --short HEAD 2>/dev/null)

if [ "$new" = "$zero" ]; then
    # ref deletion: the default branch is always protected
    if [ -n "$def" ] && [ "$ref" = "refs/heads/$def" ]; then
        echo "git-trace: refusing to delete the default branch" >&2
        exit 1
    fi
elif [ -n "$def" ] && [ "$ref" = "refs/heads/$def" ] && \
   [ "$old" != "$zero" ]; then
    # the default branch additionally rejects non-fast-forward pushes
    if ! git merge-base --is-ancestor "$old" "$new" 2>/dev/null; then
        echo "git-trace: refusing non-fast-forward push to $def" >&2
        exit 1
    fi
fi

# branch_rules + storage quota + push audit: run last so a rejected
# push is never journaled as accepted.  Deletions reach branch-check
# too (no_delete rules).  GIT_TRACE_BIN and env are baked in by mkrepo.
if [ -n "${GIT_TRACE_BIN:-}" ]; then
    "$GIT_TRACE_BIN" branch-check "$ref" "$old" "$new" || exit 1
    "$GIT_TRACE_BIN" quota-check "$ref" "$new" || exit 1
fi

exit 0