Push safety quick reference¶
Client-side safe force-push¶
| Command | What it protects |
|---|---|
git push --force-with-lease origin feat/x |
Refuses if remote ref moved since last fetch |
git push --force-with-lease=feat/x:<sha> |
Explicit lease with expected old SHA |
git push --force-if-includes origin feat/x |
Also checks remote tip is reachable locally |
git push --atomic origin main v1.0 |
All or nothing — no partial push |
git push --signed origin main |
Signs the push certificate for audit |
Default config (one-time setup)¶
Server-side deny rules¶
Set on the receiving (bare) repository:
git config receive.denyNonFastForwards true # blocks force-push
git config receive.denyDeletes true # blocks ref deletion
git config receive.fsckObjects true # validates every object
git config receive.certNonceSecret "$(openssl rand -hex 32)" # requires signed pushes
git config transfer.fsckObjects true # validates clones/fetches
git config receive.maxObjectSize 10485760 # reject objects > 10 MB
CI pipeline push patterns¶
# Squash-and-push for feature branches (safe force-push)
git fetch origin main
git rebase origin/main
git push --force-with-lease --force-if-includes origin "$CI_BRANCH"
# Release push (atomic — commit + tag must land together)
git push --atomic origin main "v$VERSION"
# Merge PR into protected main (no force required)
git merge --no-ff "$MERGE_SOURCE"
git push origin HEAD:main
Pre-receive hook: enforce signing on main¶
while read old new ref; do
case "$ref" in refs/heads/main)
for sha in $(git rev-list "$old..$new"); do
git verify-commit "$sha" &>/dev/null || { echo "Rejected: unsigned commit"; exit 1; }
done
esac
done
exit 0
Audit post-receive hook¶
while read old new ref; do
echo "$(date -Iseconds) | $ref | $old..$new | ${GIT_PUSH_OPTION_0:-unknown}" >> /var/log/git-push-audit.log
done
Inspect push safety config¶
```bash git config --local --list | grep -E '^receive.' git config --global --list | grep -E '^push.' git config --global --list | grep -E '^transfer.' git ls-remote origin # confirm remote refs before push