Add LFS config and pre-push hook

This commit is contained in:
Silicon 2026-03-30 11:21:30 +08:00
parent 7a9e8c78bb
commit bcb7daa62e
6 changed files with 274 additions and 0 deletions

27
.gitattributes vendored Normal file
View file

@ -0,0 +1,27 @@
# Git LFS 跟踪规则
# 提交到每个启用 LFS 的 GitHub 仓库根目录
# 策略:按文件类型全覆盖(含小图,可接受此开销)
# Issue: OS-GBL-F-002
# 视频
*.mp4 filter=lfs diff=lfs merge=lfs -text
*.mov filter=lfs diff=lfs merge=lfs -text
*.avi filter=lfs diff=lfs merge=lfs -text
*.wmv filter=lfs diff=lfs merge=lfs -text
# 设计稿
*.psd filter=lfs diff=lfs merge=lfs -text
*.ai filter=lfs diff=lfs merge=lfs -text
*.sketch filter=lfs diff=lfs merge=lfs -text
*.fig filter=lfs diff=lfs merge=lfs -text
# 图片
*.png filter=lfs diff=lfs merge=lfs -text
*.jpg filter=lfs diff=lfs merge=lfs -text
*.jpeg filter=lfs diff=lfs merge=lfs -text
*.gif filter=lfs diff=lfs merge=lfs -text
*.webp filter=lfs diff=lfs merge=lfs -text
*.tiff filter=lfs diff=lfs merge=lfs -text
# 如特定仓库需要某类型不走 LFS在该仓库的 .gitattributes 追加覆盖规则:
# *.png -filter -diff -merge

4
.githooks/post-checkout Executable file
View file

@ -0,0 +1,4 @@
#!/bin/sh
# LFS post-checkout passthrough (required when core.hooksPath = .githooks)
command -v git-lfs >/dev/null 2>&1 || { git lfs version >/dev/null 2>&1 || exit 0; }
git lfs post-checkout "$@"

4
.githooks/post-commit Executable file
View file

@ -0,0 +1,4 @@
#!/bin/sh
# LFS post-commit passthrough (required when core.hooksPath = .githooks)
command -v git-lfs >/dev/null 2>&1 || { git lfs version >/dev/null 2>&1 || exit 0; }
git lfs post-commit "$@"

4
.githooks/post-merge Executable file
View file

@ -0,0 +1,4 @@
#!/bin/sh
# LFS post-merge passthrough (required when core.hooksPath = .githooks)
command -v git-lfs >/dev/null 2>&1 || { git lfs version >/dev/null 2>&1 || exit 0; }
git lfs post-merge "$@"

233
.githooks/pre-push Executable file
View file

@ -0,0 +1,233 @@
#!/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

2
.lfsconfig Normal file
View file

@ -0,0 +1,2 @@
[lfs]
url = http://silicon:5e2aa6ff8dd2042a65eaaac0e8e5028bf3c99968@100.64.144.118:3000/silicon/lfs-hook-test.git/info/lfs