如何为GIT设置全局勾子,为每次提交追加信息

如何为GIT设置全局勾子,为每次提交追加信息
一、原理与数据流Git 只认一个core.hooksPath指向的目录里必须有名为pre-commit、prepare-commit-msg等文件Git 按文件名调用。因此目录里放多个同名符号链接全部指向同一个可执行脚本global-git-hooks。脚本如何知道当前是哪个钩子Git 执行的是…/hooks/pre-commit这类路径$0为 symlink 路径basename $0得到pre-commit、prepare-commit-msg等在脚本里case分发。与 Husky 的关系若core.hooksPath.husky/_只会跑 Husky 自带桩脚本不会进本目录。若core.hooksPath$HOME/.config/git/hooks或你自定义的目录由global-git-hooks按顺序调用.husky/钩子名与.git/hooks/钩子名。仓库package.json的prepare里在husky之后有条件地把本仓库core.hooksPath指到全局目录本机有global-git-hooks才合并没有的同事保持 Husky 默认.husky/_互不干扰。prepare-commit-msg顺序与「其它钩子」不同.git/hooks/prepare-commit-msg若存在且可执行 → .husky/prepare-commit-msg若存在 → 满足 PREFIX 且未设 NO_AI 时给说明文件首行末追加「 [AI]」其它已链接钩子pre-commit、commit-msg等.husky/同名若存在 → .git/hooks/同名若存在最后一个用 exec_run_husky必须先shift钩子名调用形式为_run_husky $HOOK $。函数内第一参数是逻辑钩子名如commit-msg必须shift掉再执行sh $_f $否则子脚本会把commit-msg当成$1误以为是提交说明文件路径导致commitlint --edit等报错。二、从零搭建流程1. 定目录约定全局钩子目录为可改下文用$HOOKS_HOME表示export HOOKS_HOME${GIT_HOOKS_HOME:-$HOME/.config/git/hooks} mkdir -p $HOOKS_HOME也可长期用环境变量GIT_HOOKS_HOME指向别的路径与package.json里prepare一致。2. 写入脚本global-git-hooks在$HOOKS_HOME/global-git-hooks保存下文「四、完整脚本global-git-hooks」中的全部内容。必须修改的一处脚本内PREFIX...改成「允许追加[AI]」的仓库根路径前缀规范化路径例如你所有公司代码都在/data/work下则写PREFIX/data/work。不匹配该前缀的仓库仍会执行链式钩子但不会改提交说明。然后chmod 700 $HOOKS_HOME/global-git-hooks3. 建立符号链接Git 要求的多个入口文件名cd $HOOKS_HOME for h in pre-commit commit-msg prepare-commit-msg pre-push post-merge post-commit pre-rebase; do ln -sf global-git-hooks $h done4. 让 Git 使用本目录二选一或都用方式 A — 本机所有仓库默认走全局钩子最简单git config --global core.hooksPath $HOOKS_HOME方式 B — 仅部分仓库、且与 Husky 合并推荐团队不设全局hooksPath或接受全局仍指向$HOOKS_HOME。在具体仓库的package.json里配置prepare见下文「五、仓库package.jsonHusky」。这样有$HOOKS_HOME/global-git-hooks的机器在pnpm install后会把该仓库的core.hooksPath指到$HOOKS_HOME没有该文件的机器保持 Husky 写入的.husky/_。5. 自检git config --get core.hooksPath test -e $HOOKS_HOME/global-git-hooks echo global-git-hooks ok在「应追加 AI」的仓库根下printf feat: test\n /tmp/cm.txt $HOOKS_HOME/prepare-commit-msg /tmp/cm.txt message head -1 /tmp/cm.txt # 期望feat: test [AI]三、环境变量与开关变量作用NO_AI仅当变量已设置值可为空时不追加[AI]。须用VAR command形式不能写NO_AI git …shell 会把NO_AI当成命令名。示例NO_AI git commit -m ...或NO_AI1 git commit -m ...多次提交可先export NO_AI用完unset NO_AIHUSKY0与 Husky 文档一致为 0 时跳过执行.husky/钩子GIT_HOOKS_HOME全局钩子目录不是默认路径时与package.json的prepare中GH...一致指向含global-git-hooks的目录四、完整脚本global-git-hooks保存为$HOOKS_HOME/global-git-hooks与下面内容一致请改其中PREFIX为你的工作区路径前缀。#!/usr/bin/env sh # 单入口各钩子名为指向本文件的 symlinkbasename $0 区分钩子名。 # prepare-commit-msg.git/hooks → .husky/prepare-commit-msg → [AI] # 其它.husky/同名 → .git/hooks/同名 HOOK$(basename $0) GIT_DIR$(git rev-parse --git-dir 2/dev/null) || exit 0 ROOT$(git rev-parse --show-toplevel 2/dev/null) || exit 0 ROOT_CANON$(cd $ROOT 2/dev/null pwd -P) || ROOT_CANON$ROOT PREFIX/Users/macadmin/Work/code _run_husky() { _hook$1 shift _f$ROOT/.husky/$_hook [ -f $_f ] || return 0 export PATH$ROOT/node_modules/.bin:$PATH _i${XDG_CONFIG_HOME:-$HOME/.config}/husky/init.sh [ -f $_i ] . $_i [ ${HUSKY-} 0 ] return 0 sh $_f $ || exit $? } case $HOOK in prepare-commit-msg) COMMIT_MSG_FILE$1 LEGACY$GIT_DIR/hooks/prepare-commit-msg if [ -x $LEGACY ]; then case $LEGACY in $0) ;; *) sh $LEGACY $ || exit $? ;; esac fi _run_husky prepare-commit-msg $ [ -z $COMMIT_MSG_FILE ] || [ ! -f $COMMIT_MSG_FILE ] exit 0 case $ROOT_CANON in $PREFIX|$PREFIX/*) ;; *) exit 0 ;; esac # 跳过追加须 export NO_AI 或 NO_AI git commit …勿写 NO_AI git会被当作命令 [ ${NO_AIx} x ] exit 0 _fl$(head -n 1 $COMMIT_MSG_FILE) _tr$(printf %s $_fl | tr -d \t\r\n) [ -z $_tr ] exit 0 printf %s $_fl | grep -qE \[AI\]$ exit 0 sed -i.bak 1s/$/ [AI]/ $COMMIT_MSG_FILE rm -f ${COMMIT_MSG_FILE}.bak exit 0 ;; pre-commit|commit-msg|pre-push|post-merge|post-commit|pre-rebase) _run_husky $HOOK $ LEG$GIT_DIR/hooks/$HOOK [ -x $LEG ] exec sh $LEG $ exit 0 ;; *) exit 0 ;; esac移植说明PREFIX改成你环境的工作区根例如 LinuxPREFIX/home/you/projects。判断用的是ROOT_CANONpwd -P需与实际磁盘路径前缀一致。sed -i.bak适配 macOS BSDsedLinux GNUsed同样可用。未列出的钩子名若自行增加ln -sf global-git-hooks my-hook需在脚本末尾case中增加分支否则落入*)直接退出。五、仓库package.json与 Husky 合并可选在已有husky的前提下让仅当本机存在全局脚本时把当前仓库的core.hooksPath指到$HOOKS_HOMEchmod失败不阻断后续配置。prepare: husky (chmod 700 .husky/* 2/dev/null || true) sh -c GH\${GIT_HOOKS_HOME:-$HOME/.config/git/hooks}\; [ -e \$GH/global-git-hooks\ ] git config core.hooksPath \$GH\ || true含义简述husky安装/更新.husky/_并可能把core.hooksPath写成.husky/_。chmod … || true避免无文件匹配等导致整条prepare失败从而跳过后面的git config。[ -e $GH/global-git-hooks ] git config core.hooksPath $GH只有本机已部署全局脚本时才覆盖为全局目录否则保持 Husky 默认。GIT_HOOKS_HOME全局目录非默认路径时在 shell 或 CI 里导出该变量与脚本内GH一致。六、commitlint与[AI]prepare-commit-msg在commit-msg之前执行标题会变成type: subject [AI]。若规则不允许尾部标记需在commitlint.config.js中放宽subject等规则。七、卸载 / 排错去掉全局默认钩子目录git config --global --unset core.hooksPath某个仓库曾写入本地core.hooksPathgit config --unset core.hooksPath未追加[AI]检查说明git config --get core.hooksPath合并生效时应为$HOOKS_HOME不是.husky/_PREFIX仓库根路径pwd -P是否以PREFIX为前缀NO_AI是否误设首行已以[AI]结尾脚本会跳过避免commit --amend重复追加提示NO_AI: command not found说明写成了NO_AI git commit应改为NO_AI git commit或NO_AI1 git commit等号与命令之间要有空格。commit-msg/commitlint报找不到文件多为旧版转发把钩子名当成$1传给commitlint请确认global-git-hooks内_run_husky已含shift与上文「一、6」一致。八、文件清单部署完成后$HOOKS_HOME/ global-git-hooks # 唯一实现可执行 pre-commit - global-git-hooks commit-msg - global-git-hooks prepare-commit-msg - global-git-hooks pre-push - global-git-hooks post-merge - global-git-hooks post-commit - global-git-hooks pre-rebase - global-git-hooks