Git 钩子与 Git LFS

5 min高级

Git钩子自动化与大文件管理

学习目标

本文是「Git」模块的第 34 篇,难度定位为高级。重点内容:Git钩子自动化与大文件管理

主要章节:

    1. Git 钩子概述
    1. 客户端钩子
    1. 服务器端钩子
    1. Git LFS (Large File Storage)
    1. 钩子最佳实践
    1. 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. 钩子最佳实践

  1. 版本控制钩子:将钩子存储在仓库中,使用脚本安装
  2. 错误处理:在钩子中添加适当的错误处理
  3. 性能考虑:确保钩子执行时间不会过长
  4. 可配置性:允许通过配置文件自定义钩子行为
  5. 文档:为钩子添加注释和文档

5.1 钩子管理脚本

 #!/bin/bash
 # hooks/install.sh
 # 安装钩子
 cp hooks/* .git/hooks/
 chmod +x .git/hooks/*
 echo "Hooks installed successfully"

6. Git LFS 最佳实践

  1. 合理选择跟踪文件:只跟踪真正的大文件
  2. 设置合理的文件大小阈值:根据项目需求设置
  3. 定期清理:使用 git lfs prune 清理过期文件
  4. 备份 LFS 存储:确保 LFS 文件的安全性
  5. 监控存储使用:定期检查 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