Git 钩子与 Git LFS
Git钩子自动化与大文件管理
学习目标
本文是「Git」模块的第 34 篇,难度定位为高级。重点内容:Git钩子自动化与大文件管理
主要章节:
-
- Git 钩子概述
-
- 客户端钩子
-
- 服务器端钩子
-
- Git LFS (Large File Storage)
-
- 钩子最佳实践
-
- Git LFS 最佳实践
- ……共 20 个章节
1. Git 钩子概述
Git 钩子是 Git 仓库中的脚本,在特定 Git 事件发生时自动执行。它们可以用于自动化工作流程、强制执行代码规范、运行测试等。
钩子类型
- 客户端钩子:在本地操作时触发
- 服务器端钩子:在服务器端操作时触发
2. 客户端钩子
2.1 常见客户端钩子
| 钩子名称 | 触发时机 | 用途 |
|---|---|---|
pre-commit | 提交前 | 代码检查、格式化、测试 |
prepare-commit-msg | 提交消息编辑器前 | 自动生成提交消息 |
commit-msg | 提交消息编辑后 | 验证提交消息格式 |
post-commit | 提交后 | 通知、触发构建 |
pre-push | 推送前 | 运行测试、检查 |
2.2 创建 pre-commit 钩子
# 进入 Git 仓库
cd /path/to/repo
# 创建 pre-commit 钩子
cat > .git/hooks/pre-commit << 'EOF'
#!/bin/bash
# 运行代码检查
echo "Running code linting..."
npm run lint
# 运行测试
echo "Running tests..."
npm test
# 检查结果
if [ $? -ne 0 ]; then
echo "Tests failed, commit aborted"
exit 1
fi
echo "Pre-commit checks passed"
EOF
# 使钩子可执行
chmod +x .git/hooks/pre-commit
2.3 创建 commit-msg 钩子
# 创建 commit-msg 钩子
cat > .git/hooks/commit-msg << 'EOF'
#!/bin/bash
# 检查提交消息格式
commit_msg=$(cat "$1")
# 正则表达式检查提交消息格式
if ! echo "$commit_msg" | grep -qE '^(feat|fix|docs|style|refactor|test|chore): .+'; then
echo "Error: Invalid commit message format"
echo "Commit message should start with: feat|fix|docs|style|refactor|test|chore:"
exit 1
fi
echo "Commit message format is valid"
EOF
# 使钩子可执行
chmod +x .git/hooks/commit-msg
3. 服务器端钩子
3.1 常见服务器端钩子
| 钩子名称 | 触发时机 | 用途 |
|---|---|---|
pre-receive | 推送接收前 | 拒绝不符合规则的推送 |
update | 分支更新时 | 对特定分支进行检查 |
post-receive | 推送接收后 | 部署、通知 |
3.2 创建 post-receive 钩子
# 在服务器仓库中创建 post-receive 钩子
cat > /path/to/repo.git/hooks/post-receive << 'EOF'
#!/bin/bash
# 部署应用
echo "Deploying application..."
# 切换到部署目录
cd /path/to/deploy
# 拉取最新代码
git pull origin main
# 安装依赖
npm install
# 构建应用
npm run build
# 重启服务
echo "Restarting service..."
systemctl restart my-app
echo "Deployment completed successfully"
EOF
# 使钩子可执行
chmod +x /path/to/repo.git/hooks/post-receive
4. Git LFS (Large File Storage)
4.1 Git LFS 概述
Git LFS 是 Git 的扩展,用于管理大文件,通过将大文件存储在外部服务器上,只在 Git 仓库中存储引用,从而减小仓库体积。
4.2 安装 Git LFS
# 安装 Git LFS
# Windows
download from https://git-lfs.github.com/
# macOS
brew install git-lfs
# Linux
sudo apt install git-lfs
# 初始化 Git LFS
git lfs install
4.3 配置 Git LFS
# 跟踪大文件
git lfs track "*.psd"
git lfs track "*.jpg"
git lfs track "*.mp4"
# 查看跟踪的文件类型
git lfs track
# 提交 .gitattributes 文件
git add .gitattributes
git commit -m "Add Git LFS tracking"
4.4 使用 Git LFS
# 正常添加和提交文件
git add large-file.psd
git commit -m "Add large file"
git push origin main
# 拉取 LFS 文件
git lfs pull
# 查看 LFS 文件
git lfs ls-files
# 验证 LFS 文件
git lfs verify
5. 钩子最佳实践
- 版本控制钩子:将钩子存储在仓库中,使用脚本安装
- 错误处理:在钩子中添加适当的错误处理
- 性能考虑:确保钩子执行时间不会过长
- 可配置性:允许通过配置文件自定义钩子行为
- 文档:为钩子添加注释和文档
5.1 钩子管理脚本
#!/bin/bash
# hooks/install.sh
# 安装钩子
cp hooks/* .git/hooks/
chmod +x .git/hooks/*
echo "Hooks installed successfully"
6. Git LFS 最佳实践
- 合理选择跟踪文件:只跟踪真正的大文件
- 设置合理的文件大小阈值:根据项目需求设置
- 定期清理:使用
git lfs prune清理过期文件 - 备份 LFS 存储:确保 LFS 文件的安全性
- 监控存储使用:定期检查 LFS 存储使用情况
6.1 Git LFS 配置示例
# .gitattributes 文件
* text=auto
*.md text
*.png binary
7. 高级钩子示例
7.1 自动更新版本号
# pre-commit 钩子
#!/bin/bash
# 自动更新版本号
if [ -f package.json ]; then
current_version=$(jq -r '.version' package.json)
# 简单的版本号递增逻辑
new_version=$(echo $current_version | awk -F. '{print $1"."$2"."$3+1}')
jq ".version = \"$new_version\"" package.json > package.json.tmp && mv package.json.tmp package.json
git add package.json
echo "Updated version to $new_version"
fi
7.2 自动生成 CHANGELOG
# post-commit 钩子
#!/bin/bash
# 自动生成 CHANGELOG
if [ ! -f CHANGELOG.md ]; then
echo "# Changelog\n" > CHANGELOG.md
fi
# 获取最新提交信息
latest_commit=$(git log -1 --pretty=%B)
# 提取提交类型和信息
if echo "$latest_commit" | grep -qE '^(feat|fix|docs|style|refactor|test|chore):'; then
commit_type=$(echo "$latest_commit" | cut -d: -f1)
commit_msg=$(echo "$latest_commit" | cut -d: -f2 | sed 's/^ //')
# 获取当前日期
current_date=$(date +"%Y-%m-%d")
# 添加到 CHANGELOG
echo "## $current_date\n\n- **$commit_type**: $commit_msg\n" | cat - CHANGELOG.md > CHANGELOG.md.tmp && mv CHANGELOG.md.tmp CHANGELOG.md
git add CHANGELOG.md
git commit --amend --no-edit
echo "Updated CHANGELOG.md"
fi
8. 常见问题与解决方案
8.1 钩子不执行
问题:钩子脚本没有执行
解决方案:确保钩子文件可执行 chmod +x .git/hooks/hook-name
8.2 Git LFS 文件下载失败
问题:Git LFS 文件无法下载
解决方案:检查网络连接,运行 git lfs pull 手动拉取
8.3 钩子执行时间过长
问题:钩子执行时间过长,影响开发效率 解决方案:优化钩子逻辑,考虑使用后台执行
8.4 Git LFS 存储不足
问题:Git LFS 存储空间不足
解决方案:清理过期文件 git lfs prune,增加存储配置
9. 工具与集成
9.1 钩子管理工具
- husky:现代 Git 钩子管理工具
- lint-staged:配合 husky 使用,只对暂存文件运行检查
9.2 Git LFS 托管服务
- GitHub:内置 Git LFS 支持
- GitLab:内置 Git LFS 支持
- Bitbucket:内置 Git LFS 支持
- 自托管:使用 Git LFS 服务器
10. 项目实战
10.1 完整的钩子配置
flowchart TD
T0["project/"]
T1[".git/"]
T2["hooks/"]
T3["pre-commit"]
T4["commit-msg"]
T5["pre-push"]
T6["hooks/"]
T7["pre-commit"]
T8["commit-msg"]
T9["pre-push"]
T10["install.sh"]
T11[".gitattributes"]
T12["package.json"]
T0 --> T1
T5 --> T6
T10 --> T11
T10 --> T12
10.2 使用 husky 管理钩子
安装 husky
npm install husky --save-dev
npx husky install
npm set-script prepare "husky install"
添加钩子
npx husky add .husky/pre-commit "npm run lint"
npx husky add .husky/commit-msg "npx commitlint --edit $1"
npx husky add .husky/pre-push "npm test"
安装与初始化
基本写法:安装 Git LFS
git lfs install
# 在当前用户范围启用 Git LFS
git lfs install
基本写法:在仓库中初始化 LFS
git lfs install --local
# 仅在当前仓库启用 LFS
git lfs install --local
基本写法:查看 LFS 版本
git lfs version
# 输出当前 Git LFS 版本号
git lfs version
跟踪大文件
基本写法:添加 LFS 跟踪规则
git lfs track "<模式>"
# 跟踪所有 mp4 视频文件
git lfs track "*.mp4"
基本写法:跟踪指定目录
git lfs track "<目录>/**"
# 跟踪 assets 目录下所有文件
git lfs track "assets/**"
基本写法:查看跟踪规则
git lfs track
# 列出当前所有 LFS 跟踪规则
git lfs track
基本写法:移除跟踪规则
git lfs untrack "<模式>"
# 移除某类文件的 LFS 跟踪
git lfs untrack "*.mp4"
基本写法:提交 .gitattributes
git add .gitattributes && git commit -m "<消息>"
# 跟踪规则变更必须提交
git add .gitattributes && git commit -m "chore: configure LFS tracking"
操作 LFS 文件
基本写法:添加大文件
git add <文件> && git commit -m "<消息>"
# 添加大文件到 LFS 跟踪
git add video.mp4 && git commit -m "feat: add intro video"
基本写法:查看 LFS 文件列表
git lfs ls-files
# 列出仓库中所有 LFS 跟踪文件
git lfs ls-files
基本写法:查看文件大小信息
git lfs ls-files --size
# 显示 LFS 文件的实际大小
git lfs ls-files --size
拉取与推送
基本写法:克隆含 LFS 的仓库
git clone <仓库URL>
# 克隆时自动拉取 LFS 文件
git clone https://github.com/org/repo.git
基本写法:跳过 LFS 内容克隆
GIT_LFS_SKIP_SMUDGE=1 git clone <仓库URL>
# 仅克隆指针文件不下载大文件内容
GIT_LFS_SKIP_SMUDGE=1 git clone https://github.com/org/repo.git
基本写法:按需下载 LFS 文件
git lfs pull
# 拉取所有 LFS 跟踪文件内容
git lfs pull
基本写法:拉取指定文件
git lfs pull --include="<路径>"
# 仅拉取指定目录下的 LFS 文件
git lfs pull --include="assets/videos/*"
基本写法:推送 LFS 文件
git push origin <分支>
# 推送时自动上传 LFS 文件
git push origin main
基本写法:仅推送 LFS 内容
git lfs push origin <分支>
# 单独推送 LFS 文件到远程
git lfs push origin main
基本写法:推送所有 LFS 对象
git lfs push --all origin <分支>
# 推送全部历史 LFS 对象
git lfs push --all origin main
检出与切换
基本写法:检出指定分支的 LFS 文件
git lfs checkout
# 用 LFS 内容替换工作区指针文件
git lfs checkout
基本写法:仅检出指定路径
git lfs checkout --include="<路径>"
# 仅检出 assets 目录的 LFS 内容
git lfs checkout --include="assets/*"
基本写法:切换分支后同步
git checkout <分支> && git lfs checkout
# 切换分支后重新检出 LFS 文件
git checkout feature && git lfs checkout
历史与迁移
基本写法:将已有文件转为 LFS
git lfs migrate import --include="<模式>"
# 将历史中的 mp4 文件迁移到 LFS
git lfs migrate import --include="*.mp4"
基本写法:迁移指定分支历史
git lfs migrate import --include="<模式>" --include-ref=<分支>
# 仅迁移 main 分支的历史文件
git lfs migrate import --include="*.mp4" --include-ref=main
基本写法:迁移所有引用
git lfs migrate import --include="<模式>" --include-ref=refs/heads/*
# 迁移所有分支的历史文件
git lfs migrate import --include="*.mp4" --include-ref=refs/heads/*
基本写法:导出 LFS 文件回普通对象
git lfs migrate export --include="<模式>"
# 取消 LFS 跟踪并还原文件
git lfs migrate export --include="*.mp4"
检查与状态
基本写法:查看 LFS 状态
git lfs status
# 显示工作区 LFS 文件状态
git lfs status
基本写法:检查 LFS 文件完整性
git lfs fsck
# 校验 LFS 对象完整性
git lfs fsck
基本写法:查看 LFS 日志
git lfs logs last
# 查看最近一次 LFS 操作日志
git lfs logs last
基本写法:列出所有 LFS 对象
git lfs ls-files --all
# 列出所有历史中的 LFS 文件
git lfs ls-files --all
远程配置
基本写法:查看 LFS 端点
git config -l | grep lfs
# 查看 LFS 相关配置
git config -l | grep lfs
基本写法:指定 LFS 服务器
git config -f .lfsconfig lfs.url <URL>
# 配置自定义 LFS 服务器地址
git config -f .lfsconfig lfs.url https://lfs.example.com/org/repo
基本写法:跳过 smudge 过滤器
git config --local lfs.smudge false
# 关闭自动下载 LFS 内容
git config --local lfs.smudge false
锁定文件(防冲突)
基本写法:锁定 LFS 文件
git lfs lock <文件>
# 锁定二进制文件防止并发编辑
git lfs lock assets/logo.psd
基本写法:查看锁定列表
git lfs locks
# 列出所有已锁定文件
git lfs locks
基本写法:解锁文件
git lfs unlock <文件>
# 释放文件锁
git lfs unlock assets/logo.psd
基本写法:强制解锁
git lfs unlock <文件> --force
# 强制解锁他人持有的锁
git lfs unlock assets/logo.psd --force
清理与优化
基本写法:清理无用 LFS 对象
git lfs prune
# 清理本地未引用的 LFS 对象
git lfs prune
基本写法:查看待清理对象
git lfs prune --dry-run
# 预览将被清理的对象
git lfs prune --dry-run
基本写法:强制保留对象
git lfs fetch --recent
# 拉取最近使用的 LFS 对象
git lfs fetch --recent