From c4e6b3f7b7d0e6a9f88703fbf7e447f7e51c2323 Mon Sep 17 00:00:00 2001 From: GeiserX <9169332+GeiserX@users.noreply.github.com> Date: Sat, 3 Oct 2026 19:06:49 +0200 Subject: [PATCH 1/2] chore(git): keep Co-Authored-By trailers out of commits made with the beads hooks --- .beads/hooks/commit-msg | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100755 .beads/hooks/commit-msg diff --git a/.beads/hooks/commit-msg b/.beads/hooks/commit-msg new file mode 100755 index 0000000..cab92aa --- /dev/null +++ b/.beads/hooks/commit-msg @@ -0,0 +1,18 @@ +#!/usr/bin/env sh +# Every commit in this repository has one author. Clones that point +# core.hooksPath at .beads/hooks skip the machine-wide git hooks, so this +# hook does the one job those had here: it removes Co-Authored-By trailer +# lines from the commit message. A mention inside a sentence is kept. +# If anything fails, the message is left as it was and the commit stops. +msg="$1" +[ -f "$msg" ] || exit 0 +tmp="$msg.strip.$$" +grep -v -i '^[[:space:]]*co-authored-by:' "$msg" > "$tmp" +[ $? -le 1 ] || { rm -f "$tmp"; exit 1; } +awk '{ line[NR] = $0 } + END { n = NR + while (n > 0 && line[n] ~ /^[[:space:]]*$/) n-- + for (i = 1; i <= n; i++) print line[i] }' "$tmp" > "$tmp.out" && + mv "$tmp.out" "$msg" || { rm -f "$tmp" "$tmp.out"; exit 1; } +rm -f "$tmp" +exit 0 From 27558ad869a5601715f136c365e9c48ab86b61c7 Mon Sep 17 00:00:00 2001 From: GeiserX <9169332+GeiserX@users.noreply.github.com> Date: Sat, 3 Oct 2026 21:13:13 +0200 Subject: [PATCH 2/2] fix(git): touch only messages that carry the trailer, and replace them atomically --- .beads/hooks/commit-msg | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/.beads/hooks/commit-msg b/.beads/hooks/commit-msg index cab92aa..2a27374 100755 --- a/.beads/hooks/commit-msg +++ b/.beads/hooks/commit-msg @@ -3,16 +3,30 @@ # core.hooksPath at .beads/hooks skip the machine-wide git hooks, so this # hook does the one job those had here: it removes Co-Authored-By trailer # lines from the commit message. A mention inside a sentence is kept. -# If anything fails, the message is left as it was and the commit stops. +# +# A message without such a line is never touched. Otherwise the cleaned +# text goes to a fresh file from mktemp, is compared with a second run, +# and is renamed over the message only when every step worked; on any +# failure the message stays as it was and the commit stops. msg="$1" [ -f "$msg" ] || exit 0 -tmp="$msg.strip.$$" -grep -v -i '^[[:space:]]*co-authored-by:' "$msg" > "$tmp" -[ $? -le 1 ] || { rm -f "$tmp"; exit 1; } -awk '{ line[NR] = $0 } - END { n = NR - while (n > 0 && line[n] ~ /^[[:space:]]*$/) n-- - for (i = 1; i <= n; i++) print line[i] }' "$tmp" > "$tmp.out" && - mv "$tmp.out" "$msg" || { rm -f "$tmp" "$tmp.out"; exit 1; } -rm -f "$tmp" +case $msg in -*) msg="./$msg" ;; esac +found=0 +LC_ALL=C grep -q -i '^[[:space:]]*co-authored-by:' "$msg" || found=$? +case $found in + 0) ;; + 1) exit 0 ;; + *) echo "commit-msg: cannot read $msg" >&2; exit 1 ;; +esac +clean() { + LC_ALL=C awk 'tolower($0) !~ /^[ \t\r\f\v]*co-authored-by:/ { line[++n] = $0 } + END { while (n > 0 && line[n] ~ /^[ \t\r\f\v]*$/) n-- + for (i = 1; i <= n; i++) print line[i] }' < "$msg" +} +tmp=$(mktemp "$msg.XXXXXX") || { echo "commit-msg: cannot create a temporary file beside $msg" >&2; exit 1; } +trap 'rm -f "$tmp"' EXIT +trap 'exit 1' HUP INT TERM +clean > "$tmp" && clean | cmp -s - "$tmp" || { echo "commit-msg: cannot rewrite $msg" >&2; exit 1; } +trap '' HUP INT TERM +mv -f "$tmp" "$msg" || { echo "commit-msg: cannot replace $msg" >&2; exit 1; } exit 0