From 9cbc7e18982ec75af0672f99c7450b925ecb5261 Mon Sep 17 00:00:00 2001 From: Mirotin Artem Date: Sat, 8 Aug 2026 01:27:34 +0300 Subject: [PATCH] fix(hooks): skip the pre-push suite on delete-only pushes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Deleting a remote branch is a push, and the hook ran the full ~3-minute gate for every deletion — cleaning 21 stale remote branches after the P-series triggered it per branch until the batch timed out. A deletion moves no code: git hands the hook local_ref="(delete)" with an all-zero local sha, so treat those lines like the tag-only case and short-circuit. Mixed pushes (a real ref + deletions) still run the suite, and an empty stdin (dry-run) still falls through, as before. Verified by feeding the hook synthetic stdin for all three scenarios. --- .githooks/pre-push | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/.githooks/pre-push b/.githooks/pre-push index 9440c04e..f97db62d 100755 --- a/.githooks/pre-push +++ b/.githooks/pre-push @@ -13,13 +13,23 @@ # 3. `git push --no-verify` — git's built-in escape hatch. set -e -# ── Tag-only push short-circuit ────────────────────────────────────────────── +# ── Tag-only / delete-only push short-circuit ──────────────────────────────── # git passes one line per ref on stdin: # +# A branch DELETION arrives as local_ref="(delete)" with an all-zero +# local_sha — no code moves, so it must not trigger the suite (observed +# 2026-08-08: cleaning 21 stale remote branches ran the full gate per +# deletion, ~3 min each). any_ref=0 any_non_tag=0 -while read -r local_ref _local_sha _remote_ref _remote_sha; do +any_delete=0 +while read -r local_ref local_sha _remote_ref _remote_sha; do [ -z "$local_ref" ] && continue + case "$local_ref,$local_sha" in + "(delete)",*|*,0000000000000000000000000000000000000000) + any_delete=1 + continue ;; + esac any_ref=1 case "$local_ref" in refs/tags/*) ;; @@ -27,6 +37,13 @@ while read -r local_ref _local_sha _remote_ref _remote_sha; do esac done +# Delete-only push: refs were listed but every one is a deletion. +# Empty stdin (dry-run) still falls through to the suite, as before. +if [ "$any_ref" = 0 ] && [ "$any_delete" = 1 ]; then + echo "=== Pre-push: delete-only push — skipping CI mirror checks ===" + exit 0 +fi + if [ "$any_ref" = 1 ] && [ "$any_non_tag" = 0 ]; then echo "=== Pre-push: tag-only push — skipping CI mirror checks ===" exit 0