{"slug": "small-git-tools-to-take-authorship-from-llm-work", "title": "Small git tools to take authorship from LLM work", "summary": "A developer created a Git tool called git_reauthor.sh that rewrites commit authorship and Co-Authored-By trailers for a range of commits. The script can replace author/committer identity, strip or replace Co-Authored-By lines, and is intended to help developers take authorship credit for work originally generated by large language models.", "body_md": "|\n#!/usr/bin/env bash |\n|\n# |\n|\n# git_reauthor.sh <back_until_sha_inclusive> <committer_name_email> <coauthored_by_name_email> |\n|\n# |\n|\n# Rewrite the identity (author AND committer name/email) of every commit from |\n|\n# <back_until_sha_inclusive> up to HEAD, and fix up the Co-Authored-By trailer. |\n|\n# |\n|\n# Arguments 2 and 3 are optional: |\n|\n# * no <committer_name_email> -> use the current git config user.name/email |\n|\n# as the identity (and, since arg 3 is then also absent, strip co-authors). |\n|\n# * no <coauthored_by_name_email> -> strip every Co-Authored-By line from the |\n|\n# commit messages. |\n|\n# * <coauthored_by_name_email> present -> replace any existing Co-Authored-By |\n|\n# line(s) with a single \"Co-Authored-By: <that value>\" (appended if the |\n|\n# commit had none). |\n|\n# |\n|\n# Identity arguments are in the usual git form: Name Surname <email@host> |\n|\n# Commit dates are left untouched. |\n|\n# |\n|\n# WARNING: this rewrites history (commit SHAs change). filter-branch keeps a |\n|\n# backup at refs/original/... ; undo with: |\n|\n# git reset --hard refs/original/refs/heads/<branch> |\n|\n# |\n|\nset -euo pipefail |\n|\n|\n|\nif [ \"$#\" -lt 1 ] || [ \"$#\" -gt 3 ]; then |\n|\necho \"usage: $(basename \"$0\") <back_until_sha_inclusive> <committer_name_email> <coauthored_by_name_email>\" >&2 |\n|\necho \" (args 2 and 3 optional; omit arg 3 to strip Co-Authored-By lines,\" >&2 |\n|\necho \" omit arg 2 to also fall back to the current git config identity)\" >&2 |\n|\nexit 2 |\n|\nfi |\n|\n|\n|\nSINCE=\"$1\" |\n|\nIDENT=\"${2:-}\" |\n|\nCOAUTHOR=\"${3:-}\" |\n|\n|\n|\n# --- validate ------------------------------------------------------------- |\n|\nif ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then |\n|\necho \"error: not inside a git work tree\" >&2 |\n|\nexit 1 |\n|\nfi |\n|\n|\n|\nif ! git rev-parse --verify --quiet \"${SINCE}^{commit}\" >/dev/null; then |\n|\necho \"error: '$SINCE' is not a valid commit\" >&2 |\n|\nexit 1 |\n|\nfi |\n|\n|\n|\n# Resolve the target identity: an explicit \"Name <email>\" argument, or the |\n|\n# current git config user.name/user.email when arg 2 is omitted. |\n|\nif [ -n \"$IDENT\" ]; then |\n|\ncase \"$IDENT\" in |\n|\n*\"<\"*\">\"*) : ;; |\n|\n*) echo \"error: committer identity must look like 'Name <email>', got '$IDENT'\" >&2; exit 1 ;; |\n|\nesac |\n|\nRA_NAME=\"$(printf '%s' \"$IDENT\" | sed -E 's/[[:space:]]*<[^>]*>[[:space:]]*$//')\" |\n|\nRA_EMAIL=\"$(printf '%s' \"$IDENT\" | sed -E 's/^[^<]*<([^>]*)>.*$/\\1/')\" |\n|\nelse |\n|\nRA_NAME=\"$(git config user.name || true)\" |\n|\nRA_EMAIL=\"$(git config user.email || true)\" |\n|\nif [ -z \"$RA_NAME\" ] || [ -z \"$RA_EMAIL\" ]; then |\n|\necho \"error: no identity given and git config user.name/user.email is unset\" >&2 |\n|\nexit 1 |\n|\nfi |\n|\nfi |\n|\n|\n|\nif [ -n \"$COAUTHOR\" ]; then |\n|\ncase \"$COAUTHOR\" in |\n|\n*\"<\"*\">\"*) : ;; |\n|\n*) echo \"error: co-author must look like 'Name <email>', got '$COAUTHOR'\" >&2; exit 1 ;; |\n|\nesac |\n|\nfi |\n|\n|\n|\n# Range that includes SINCE itself. If SINCE is the root commit it has no |\n|\n# parent, so fall back to the full history reachable from HEAD. |\n|\nif git rev-parse --verify --quiet \"${SINCE}^{commit}^\" >/dev/null; then |\n|\nRANGE=\"${SINCE}~1..HEAD\" |\n|\nelse |\n|\nRANGE=\"HEAD\" |\n|\nfi |\n|\n|\n|\n# --- awk program that rewrites the commit message ------------------------- |\n|\n# Removes existing Co-Authored-By lines, trims trailing blank lines, and (when |\n|\n# ca is non-empty) appends a single Co-Authored-By trailer, keeping it inside |\n|\n# the trailing trailer block when one already exists. |\n|\nAWK_PROG=\"$(mktemp \"${TMPDIR:-/tmp}/reauthor.XXXXXX\")\" |\n|\ntrap 'rm -f \"$AWK_PROG\"' EXIT |\n|\ncat > \"$AWK_PROG\" <<'AWK' |\n|\n{ buf[NR] = $0 } |\n|\nEND { |\n|\nm = 0 |\n|\nfor (i = 1; i <= NR; i++) { |\n|\nif (tolower(buf[i]) ~ /^co-authored-by:/) continue |\n|\nout[++m] = buf[i] |\n|\n} |\n|\nwhile (m > 0 && out[m] ~ /^[ \\t]*$/) m-- # trim trailing blanks |\n|\nfor (i = 1; i <= m; i++) print out[i] |\n|\nif (ca != \"\") { |\n|\nif (m > 0 && out[m] ~ /^[A-Za-z0-9-]+:[ \\t]/) { # last line already a trailer |\n|\nprint \"Co-Authored-By: \" ca |\n|\n} else { |\n|\nif (m > 0) print \"\" # blank line before trailer |\n|\nprint \"Co-Authored-By: \" ca |\n|\n} |\n|\n} |\n|\n} |\n|\nAWK |\n|\n|\n|\n# --- rewrite -------------------------------------------------------------- |\n|\nexport RA_NAME RA_EMAIL COAUTHOR |\n|\n|\n|\nENV_FILTER=' |\n|\nexport GIT_AUTHOR_NAME=\"$RA_NAME\" |\n|\nexport GIT_AUTHOR_EMAIL=\"$RA_EMAIL\" |\n|\nexport GIT_COMMITTER_NAME=\"$RA_NAME\" |\n|\nexport GIT_COMMITTER_EMAIL=\"$RA_EMAIL\" |\n|\n' |\n|\nMSG_FILTER=\"awk -v ca=\\\"\\$COAUTHOR\\\" -f \\\"$AWK_PROG\\\"\" |\n|\n|\n|\nif [ -n \"$COAUTHOR\" ]; then |\n|\necho \"Reauthoring $RANGE as '$RA_NAME <$RA_EMAIL>', co-author -> '$COAUTHOR'\" |\n|\nelse |\n|\necho \"Reauthoring $RANGE as '$RA_NAME <$RA_EMAIL>', stripping Co-Authored-By lines\" |\n|\nfi |\n|\n|\n|\nFILTER_BRANCH_SQUELCH_WARNING=1 git filter-branch -f \\ |\n|\n--env-filter \"$ENV_FILTER\" \\ |\n|\n--msg-filter \"$MSG_FILTER\" \\ |\n|\n-- $RANGE |\n|\n|\n|\necho |\n|\necho \"Done. Backup ref kept at refs/original/. Undo with:\" |\n|\necho \" git reset --hard refs/original/refs/heads/$(git rev-parse --abbrev-ref HEAD)\" |", "url": "https://wpnews.pro/news/small-git-tools-to-take-authorship-from-llm-work", "canonical_source": "https://gist.github.com/alganet/2437625c4dc7c8f44ccf19aed2cfc996", "published_at": "2026-07-15 14:54:43+00:00", "updated_at": "2026-07-15 16:34:10.665941+00:00", "lang": "en", "topics": ["developer-tools", "ai-tools"], "entities": [], "alternates": {"html": "https://wpnews.pro/news/small-git-tools-to-take-authorship-from-llm-work", "markdown": "https://wpnews.pro/news/small-git-tools-to-take-authorship-from-llm-work.md", "text": "https://wpnews.pro/news/small-git-tools-to-take-authorship-from-llm-work.txt", "jsonld": "https://wpnews.pro/news/small-git-tools-to-take-authorship-from-llm-work.jsonld"}}