aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorLibravatar sommerfeld <sommerfeld@sommerfeld.dev>2026-06-17 15:47:38 +0100
committerLibravatar sommerfeld <sommerfeld@sommerfeld.dev>2026-06-17 15:47:38 +0100
commit21cf68aa34b9b6515c4a2668f656fc5b767ec9c1 (patch)
treef4f2f197bc615dd49803c24748c95a646860e795
parente86e1a2e2471613169d0f016abf87f264de69003 (diff)
downloaddotfiles-21cf68aa34b9b6515c4a2668f656fc5b767ec9c1.tar.gz
dotfiles-21cf68aa34b9b6515c4a2668f656fc5b767ec9c1.tar.bz2
dotfiles-21cf68aa34b9b6515c4a2668f656fc5b767ec9c1.zip
Relax AI committer push check
-rw-r--r--README.md2
-rwxr-xr-xdot_config/git/hooks/executable_pre-push32
2 files changed, 11 insertions, 23 deletions
diff --git a/README.md b/README.md
index 1da3e7f..d8e603b 100644
--- a/README.md
+++ b/README.md
@@ -143,7 +143,7 @@ Projects opt in by just dropping a file at `.githooks/<name>` — no `core.hooks
- `pre-commit` → repo's `.githooks/pre-commit` (if any). No global logic. In this repo: `just check`.
- `commit-msg` → repo's `.githooks/commit-msg` (if any), then strips any `Co-authored-by:` whose identity matches an AI agent (Copilot/Claude/Codex/…) so they don't trip the push gate.
-- `pre-push` → repo's `.githooks/pre-push` (if any), then rejects pushes that contain unsigned commits, commits with a foreign committer, or commits authored/co-authored by an AI agent.
+- `pre-push` → repo's `.githooks/pre-push` (if any), then rejects pushes that contain unsigned commits or commits whose author/committer/coauthor looks like an AI agent.
- `post-commit` → repo's `.githooks/post-commit` (if any). No global logic. In this repo: `chezmoi apply`.
Bypass any of these with `--no-verify` on `commit`/`push`.
diff --git a/dot_config/git/hooks/executable_pre-push b/dot_config/git/hooks/executable_pre-push
index b0915bf..0d4a154 100755
--- a/dot_config/git/hooks/executable_pre-push
+++ b/dot_config/git/hooks/executable_pre-push
@@ -1,11 +1,11 @@
#!/usr/bin/env dash
# Reject pushes that include commits which:
# * lack a good signature, or
-# * have a committer different from this repo's user.name / user.email, or
-# * have an author that looks like a coding agent (Copilot, Claude,
-# Codex, ChatGPT, Cursor, Aider, Devin, ...) -- I want my name on
-# anything I push, even when an agent helped write it. Use
-# `git commit --amend --reset-author` after agent-assisted work.
+# * have an author, committer, or coauthor that looks like a coding
+# agent (Copilot, Claude, Codex, ChatGPT, Cursor, Aider, Devin, ...).
+# I want human names on anything I push, even when an agent helped
+# write it. Use `git commit --amend --reset-author` after
+# agent-authored work.
#
# Activated via core.hooksPath in ~/.config/git/config so it applies to
# every repo unless that repo overrides hooksPath itself (this dotfiles
@@ -26,14 +26,6 @@ dispatch_repo_hook pre-push "$@" <"$_stdin_buf"
zero=$(git hash-object --stdin </dev/null | tr '0-9a-f' '0')
-expected_name=$(git config user.name || true)
-expected_email=$(git config user.email || true)
-
-if [ -z "$expected_name" ] || [ -z "$expected_email" ]; then
- printf 'pre-push: user.name or user.email is unset; refusing.\n' >&2
- exit 1
-fi
-
# %G? signature status codes from git-log:
# G good signature
# U good, unknown validity (e.g. trust level not set)
@@ -46,7 +38,7 @@ fi
# We accept G/U/X/Y and reject anything else.
ok='G U X Y'
-# Case-insensitive substrings that disqualify an author. Matched against
+# Case-insensitive substrings that disqualify an identity. Matched against
# "<name> <email>" lowercased. Plain substring matching (index()) is
# used in the awk below to dodge regex-escaping pitfalls. Keep this list
# narrow on purpose: false positives are worse than misses (a slipped
@@ -76,8 +68,6 @@ while read -r _local_ref local_sha remote_ref remote_sha; do
--no-commit-header "$@" |
awk -F'\t' \
-v ok="$ok" \
- -v en="$expected_name" \
- -v ee="$expected_email" \
-v agent_subs="$agent_subs" '
BEGIN {
split(ok, a, " "); for (i in a) good[a[i]] = 1
@@ -90,8 +80,8 @@ while read -r _local_ref local_sha remote_ref remote_sha; do
{
reasons = ""
if (!($2 in good)) reasons = reasons " [sig=" $2 "]"
- if ($3 != en || $4 != ee) {
- reasons = reasons " [committer=" $3 " <" $4 ">]"
+ if (is_agent(tolower($3 " " $4))) {
+ reasons = reasons " [agent-committer=" $3 " <" $4 ">]"
}
if (is_agent(tolower($5 " " $6))) {
reasons = reasons " [agent-author=" $5 " <" $6 ">]"
@@ -111,8 +101,6 @@ while read -r _local_ref local_sha remote_ref remote_sha; do
if [ -n "$bad" ]; then
if [ "$fail" -eq 0 ]; then
printf '\nrefusing to push: bad commits found\n' >&2
- printf '(expected committer: %s <%s>)\n' \
- "$expected_name" "$expected_email" >&2
fi
printf '\non %s:\n%s\n' "$remote_ref" "$bad" >&2
fail=1
@@ -120,9 +108,9 @@ while read -r _local_ref local_sha remote_ref remote_sha; do
done <"$_stdin_buf"
if [ "$fail" -ne 0 ]; then
- printf '\nfix sig + committer:\n' >&2
+ printf '\nfix signature:\n' >&2
printf ' git rebase --exec "git commit --amend --no-edit -S" <base>\n' >&2
- printf 'fix author (re-stamp yourself as author, keep agent out):\n' >&2
+ printf 'fix AI identity (re-stamp yourself as author/committer, keep agent out):\n' >&2
printf ' git rebase --exec "git commit --amend --no-edit --reset-author -S" <base>\n' >&2
printf 'bypass:\n git push --no-verify\n\n' >&2
exit 1