能否创建基于待推送commit对应tag运行脚本的pre-push hook?
一、pre-push hook 是否满足需求
完全可以。
pre-push 是Git原生支持的客户端钩子,触发时机在本地执行git push后、实际向远端传输数据之前,这个阶段你可以完整获取所有待推送的提交、标签信息,脚本如果返回非0状态码还可以直接中断push流程,完全适配你要在push时提取tag生成归档的需求。
需要注意的是,pre-push 钩子属于本地钩子,默认不会随Git仓库同步到其他协作者本地,如果要给团队统一使用,需要额外配置钩子的自动下发/安装逻辑。
二、单个commit引用与关联tag的获取方法
1. 待推送commit的获取逻辑
pre-push 钩子运行时,Git会通过标准输入向脚本逐行传入待推送的引用信息,每行固定格式为:<本地引用完整路径> <本地引用指向的commit哈希> <远端引用完整路径> <远端引用当前指向的commit哈希>
你只需要逐行读取标准输入的内容,就可以拿到所有待推送的引用:
- 如果是删除分支/标签的操作,本地传入的commit哈希会是全0值,这类场景直接跳过即可
- 如果远端对应引用还不存在(比如第一次推送新分支、新标签),待推送的commit范围就是本地引用指向的所有可达commit
- 如果远端已有对应引用,通过
git rev-list <远端commit哈希>..<本地commit哈希>就可以列出所有待推送的单个commit哈希
2. 单个commit关联tag的获取
要获取直接绑定在某个commit上的tag,直接执行以下命令即可:git tag --points-at <目标commit哈希>
命令会直接输出所有打在该commit上的标签名,无论是轻量标签还是附注标签都能正常识别。
如果你还需要获取包含该commit的后续标签(即标签打在当前commit之后的提交上,当前commit属于标签对应代码版本的一部分),可以把参数换成--contains:git tag --contains <目标commit哈希>
根据你要提取commit绑定tag的需求,用--points-at参数就可以满足要求。
最小可用脚本示例
你可以把以下内容放到仓库.git/hooks/pre-push文件中,赋予可执行权限后即可生效:
#!/bin/sh # 生成全0哈希,用于判断删除引用的场景 ZERO_HASH=$(git hash-object --stdin </dev/null | tr '0-9a-f' '0') # 提前创建归档存放目录 mkdir -p ./archives # 逐行读取pre-push传入的引用信息 while read local_ref local_sha remote_ref remote_sha; do # 跳过删除分支/tag的场景 if [ "$local_sha" = "$ZERO_HASH" ]; then continue fi # 确定待遍历的commit范围 if [ "$remote_sha" = "$ZERO_HASH" ]; then commit_range="$local_sha" else commit_range="${remote_sha}..${local_sha}" fi # 遍历范围内所有单个commit for commit in $(git rev-list "$commit_range"); do # 获取当前commit直接绑定的tag bind_tags=$(git tag --points-at "$commit") if [ -n "$bind_tags" ]; then for tag in $bind_tags; do # 生成与tag同名的tar.gz归档文件 git archive --format=tar.gz -o "./archives/${tag}.tar.gz" "$tag" echo "已为标签 $tag 生成归档文件: ./archives/${tag}.tar.gz" done fi done done exit 0
内容的提问来源于stack exchange,提问作者LoganSonOfGrim

