推送閘改成判目標,不判整條指令裡有沒有那個字

一個晚上誤攔六次,全都不是在推預設分支:
  ① checkout -b 建新分支時把預設分支寫在後面,再推那條新分支
  ② gh pr create 指定 base——根本不是 git push
  ③ 推 tag(refs/tags/…)
  ④ 推 feature 分支(帶 -u)
  ⑤ 它擋住了我用來**測試它自己**的那條指令
  ⑥ 它擋住了這一筆的 commit——因為 message 裡引用了那幾個字

leo 2026-08-17 早就講過這個形狀:文字層封路必敗,
「紅線寫得越細,命中關鍵字的機率越高 ⇒ 那些閘在懲罰謹慎」。
舊版掃整條指令字串,正是文字層。

改成解析 push 的目標 refspec:
  旗標跳過/第一個非旗標=remote/a:b 取 b/refs/tags/* 不算分支
  一個 refspec 都沒給,才退回看當前分支

八向實測(hooks/tests/main-and-prod-push-guard.test.sh,8/8):
  五種該放行的(今晚誤攔的原形狀,含分支名帶 domain 那種)全過
  三種該擋的全擋

中途自己抓到一個 bug:tag 被跳過後目標清單變空 → 退回猜當前分支
⇒ 當前分支剛好叫預設名時誤擋。改成看到 refspec 就不退回猜測。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-21 01:29:55 +08:00
parent 43c328d26f
commit 67dae3b814
5 changed files with 98 additions and 45 deletions
+27 -1
View File
@@ -125,7 +125,33 @@ if printf '%s' "$CMD" | grep -qE '(^|[;&|(`]|&&|\|\|)[[:space:]]*git([[:space:]]
# 2026-08-20: match the target branch on a word boundary, not a bare substring --
# a glob like *main* also matches "domain" (d-o-**m-a-i-n**), e.g. a push to
# `fix/custom-domain-setup` would have false-positived.
if printf '%s' "$CMD" | grep -qE '(^|[^A-Za-z])(main|master)([^A-Za-z]|$)'; then
# 2026-08-21: 只看 **push 的目標**,不再掃整條指令。
# 舊版掃整條 ⇒ 一個晚上誤攔四次,全都是推 feature branch 或 tag
# git checkout -b fix/x main && git push origin fix/x ← 「main」在 checkout 上
# gh pr create --base main ← 根本不是 git push
# git checkout origin/main --detach; git push origin refs/tags/v0.3.3
# ⇒ **紅線寫得越細,命中關鍵字的機率越高**(leo 2026-08-17 的觀察,
# 文字層封路必敗)。這裡改成判動作的目標,不是判字面。
_push_seg=$(printf '%s' "$CMD" | sed -E 's/.*git[[:space:]]+(-[^[:space:]]+[[:space:]]+)*push//' | sed -E 's/[;&|].*//')
_dest=""
_seen_remote=0
_saw_refspec=0
for _tok in $_push_seg; do
case "$_tok" in
-*) continue ;; # 旗標
refs/tags/*|*:refs/tags/*) _saw_refspec=1; continue ;; # 推 tag 不是推分支
esac
if [ "$_seen_remote" = "0" ]; then _seen_remote=1; continue; fi # 第一個非旗標=remote
_saw_refspec=1
_dest="$_dest ${_tok##*:}" # a:b 的目標是 b;沒有冒號就是它自己
done
# 🔴 只有「一個 refspec 都沒給」才退回猜當前分支。
# 看到 refspec(哪怕是 tag)就照它判——否則推 tag 會被當成推當前分支,
# 而當前分支若剛好叫 main 就誤擋(2026-08-21 實測抓到)。
if [ "${_saw_refspec:-0}" = "0" ]; then
_dest=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")
fi
if printf '%s' "$_dest" | tr ' ' '\n' | grep -qE '^(main|master)$'; then
stamp_ok && exit 0
# ── 擋下的同時,把「誰想推什麼」留成一份請求(leo 2026-08-12)───────────