启用pre-commit hook后Git提交极慢,求性能合理性分析
提交文件大小限制Hook性能问题解答
延迟是否正常?
完全不正常。你的硬件配置足够强劲,1000个文件提交耗时从3秒暴涨到300秒,核心问题出在原脚本的进程调用效率极低。
性能瓶颈原因
原脚本在循环里对每个文件单独执行git ls-files -s和git cat-file -s两个命令:
- 每调用一次git命令都会启动一个全新的子进程,1000个文件就要启动2000次进程,进程创建、销毁的开销累积起来直接拖垮了速度。
- 再加上每次都要解析命令输出、切割字符串,进一步放大了耗时。
优化方案:批量处理减少git调用
通过一次性批量获取所有暂存文件的哈希和大小,把git命令的调用次数从2000次压缩到3-4次,性能会直接回到正常水平。优化后的脚本如下:
#!/bin/bash hard_limit=$(git config hooks.filesizehardlimit) soft_limit=$(git config hooks.filesizesoftlimit) : ${hard_limit:=1000000} : ${soft_limit:=500000} status=0 bytesToHuman() { b=${1:-0}; d=''; s=0; S=({,K,M,G,T,P,E,Z,Y}B) while ((b > 1000)); do d="$(printf ".%01d" $((b % 1000 * 10 / 1000)))" b=$((b / 1000)) let s++ done echo "$b$d${S[$s]}" } # 批量关联文件路径、哈希与大小,避免循环内重复调用git join -t $'\t' \ <(git diff --staged --name-only --diff-filter=d -z | xargs -0 git ls-files -s | awk '{print $2 "\t" $NF}' | sort) \ <(git diff --staged --name-only --diff-filter=d -z | xargs -0 git ls-files -s | awk '{print $2}' | git cat-file --batch-check='%(objectname) %(objectsize)' | sort) \ | while IFS=$'\t' read -r hash file size; do if (( size > hard_limit )); then echo "Error: Cannot commit '$file' because it is $(bytesToHuman $size), which exceeds the hard size limit of $(bytesToHuman $hard_limit)." status=1 elif (( size > soft_limit )); then echo "Warning: '$file' is $(bytesToHuman $size), which exceeds the soft size limit of $(bytesToHuman $soft_limit). Please double check that you intended to commit this file." fi done exit $status
优化说明
- 用
git diff --staged --name-only -z批量获取暂存文件列表,配合xargs -0处理含特殊字符的文件名。 - 一次调用
git ls-files -s获取所有文件的哈希和路径。 - 一次调用
git cat-file --batch-check批量查询所有哈希对应的文件大小。 - 用
join命令将路径和大小关联起来,最后循环处理每个文件的大小限制检查。
这样修改后,提交1000个文件的耗时应该会回到和禁用hook时接近的水平。
内容的提问来源于stack exchange,提问作者iostrym
相关产品推荐
相关产品推荐

