lfs-hook-test/.githooks/pre-push

233 lines
6.3 KiB
Bash
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/sh
# LFS Pre-Push Hook — push 前显示 LFS 文件清单并要求确认
# Issue: OS-GBL-F-002
#
# 功能:
# 1. 检测本次 push 涉及的 LFS 文件,显示清单和总大小
# 2. >500MB 的文件标记警告
# 3. 用户确认后执行 git lfs pre-push 上传
#
# 环境变量:
# LFS_PREPUSH_SKIP=1 跳过确认CI/CD 用)
#
# 兼容性POSIX sh + BSD awkmacOS 原生环境)
REMOTE="$1"
URL="$2"
# --- 捕获 stdinref 数据),后续 replay 给 git lfs pre-push ---
INPUT=""
while IFS= read -r line; do
if [ -z "$INPUT" ]; then
INPUT="$line"
else
INPUT="$INPUT
$line"
fi
done
# --- 检查 git-lfs 是否可用 ---
if ! command -v git-lfs >/dev/null 2>&1 && ! git lfs version >/dev/null 2>&1; then
echo "pre-push: git-lfs not found, skipping LFS check"
exit 0
fi
# --- 收集所有 ref 涉及的 LFS 文件 ---
LFS_LIST=""
process_ref() {
local_ref="$1"
local_sha="$2"
remote_ref="$3"
remote_sha="$4"
ZERO="0000000000000000000000000000000000000000"
# 删除分支 → 跳过
if [ "$local_sha" = "$ZERO" ]; then
return
fi
# 确定比较基准
if [ "$remote_sha" = "$ZERO" ]; then
# 新分支:找与默认分支的 merge-base
base=""
for candidate in main master develop; do
if git rev-parse --verify "refs/remotes/origin/$candidate" >/dev/null 2>&1; then
base=$(git merge-base "refs/remotes/origin/$candidate" "$local_sha" 2>/dev/null)
break
fi
done
if [ -z "$base" ]; then
# 无法找到基准,列出该分支所有 LFS 文件
base=""
fi
else
base="$remote_sha"
fi
# 获取 LFS 文件列表(带大小)
if [ -n "$base" ]; then
files=$(git lfs ls-files --size "$base" "$local_sha" 2>/dev/null)
else
files=$(git lfs ls-files --size "$local_sha" 2>/dev/null)
fi
if [ -n "$files" ]; then
LFS_LIST="$LFS_LIST
$files"
fi
}
# 解析每一行 ref 数据
if [ -n "$INPUT" ]; then
echo "$INPUT" | while IFS=' ' read -r local_ref local_sha remote_ref remote_sha; do
if [ -n "$local_ref" ]; then
process_ref "$local_ref" "$local_sha" "$remote_ref" "$remote_sha"
fi
done
fi
# 因为上面的 while 在子 shell 中LFS_LIST 不会传递回来
# 重新在当前 shell 处理
LFS_LIST=""
if [ -n "$INPUT" ]; then
set -f # 禁用 glob
OLD_IFS="$IFS"
IFS='
'
for line in $INPUT; do
IFS="$OLD_IFS"
local_ref=$(echo "$line" | awk '{print $1}')
local_sha=$(echo "$line" | awk '{print $2}')
remote_ref=$(echo "$line" | awk '{print $3}')
remote_sha=$(echo "$line" | awk '{print $4}')
ZERO="0000000000000000000000000000000000000000"
# 删除分支 → 跳过
if [ "$local_sha" = "$ZERO" ]; then
continue
fi
# 确定比较基准
base=""
if [ "$remote_sha" = "$ZERO" ]; then
for candidate in main master develop; do
if git rev-parse --verify "refs/remotes/origin/$candidate" >/dev/null 2>&1; then
base=$(git merge-base "refs/remotes/origin/$candidate" "$local_sha" 2>/dev/null)
break
fi
done
else
base="$remote_sha"
fi
# 获取 LFS 文件列表
if [ -n "$base" ]; then
files=$(git lfs ls-files --size "$base" "$local_sha" 2>/dev/null)
else
files=$(git lfs ls-files --size "$local_sha" 2>/dev/null)
fi
if [ -n "$files" ]; then
LFS_LIST="$LFS_LIST
$files"
fi
IFS='
'
done
IFS="$OLD_IFS"
set +f
fi
# --- 去重并解析 ---
# git lfs ls-files --size 输出格式: <oid> <indicator> <path> (<size>)
# 示例: abc1234567 * assets/logo.png (1.2 MB)
if [ -z "$LFS_LIST" ] || [ "$(echo "$LFS_LIST" | sed '/^$/d' | wc -l)" -eq 0 ]; then
# 无 LFS 文件 → 静默执行 git lfs pre-push 并放行
echo "$INPUT" | git lfs pre-push "$@"
exit $?
fi
# 按文件路径去重
DEDUPED=$(echo "$LFS_LIST" | sed '/^$/d' | awk '!seen[$3]++')
FILE_COUNT=$(echo "$DEDUPED" | wc -l | awk '{print $1}')
# --- 非 TTY 或 CI 模式 → 自动放行 ---
if [ "${LFS_PREPUSH_SKIP:-0}" = "1" ]; then
echo "$INPUT" | git lfs pre-push "$@"
exit $?
fi
if [ ! -t 0 ] && [ ! -t 1 ]; then
# 非交互环境,自动放行
echo "$INPUT" | git lfs pre-push "$@"
exit $?
fi
# --- 显示 LFS 文件清单 ---
echo ""
echo "=========================================="
echo " LFS Pre-Push: $FILE_COUNT file(s) detected"
echo "=========================================="
echo ""
TOTAL_BYTES=0
HAS_LARGE=0
echo "$DEDUPED" | while IFS= read -r entry; do
# 提取路径和大小
path=$(echo "$entry" | awk '{print $3}')
size_str=$(echo "$entry" | sed 's/.*(\(.*\))/\1/')
# 解析大小为字节数(用于判断 >500MB
size_num=$(echo "$size_str" | awk '{print $1}')
size_unit=$(echo "$size_str" | awk '{print $2}')
bytes=0
case "$size_unit" in
B) bytes=$(echo "$size_num" | awk '{printf "%.0f", $1}') ;;
KB) bytes=$(echo "$size_num" | awk '{printf "%.0f", $1 * 1024}') ;;
MB) bytes=$(echo "$size_num" | awk '{printf "%.0f", $1 * 1048576}') ;;
GB) bytes=$(echo "$size_num" | awk '{printf "%.0f", $1 * 1073741824}') ;;
esac
# 检查是否超过 500MB (524288000 bytes)
is_large=$(echo "$bytes" | awk '{if ($1 > 524288000) print 1; else print 0}')
if [ "$is_large" = "1" ]; then
printf " %-50s %s %s\n" "$path" "$size_str" "[!] >500MB"
else
printf " %-50s %s\n" "$path" "$size_str"
fi
done
echo ""
echo "------------------------------------------"
echo " Total: $FILE_COUNT file(s)"
echo "------------------------------------------"
echo ""
# --- 确认提示 ---
printf "Proceed with LFS upload? [y/N] "
# 从 /dev/tty 读取用户输入(因为 stdin 已被 git 占用)
if read -r answer < /dev/tty 2>/dev/null; then
case "$answer" in
[yY]|[yY][eE][sS])
echo "Uploading LFS files..."
echo "$INPUT" | git lfs pre-push "$@"
exit $?
;;
*)
echo "Push aborted by user."
exit 1
;;
esac
else
echo "Cannot read from terminal, aborting."
exit 1
fi