跳转到内容
关系图谱

Git LFS 与大文件管理详解

3,560 字 14 分钟

属性 1
status
学习中

Git LFS 与大文件管理详解

Git 原生设计不适合管理大文件——每次 clone 都要下载完整历史中的每个版本。Git LFS(Large File Storage)通过将大文件存储在独立服务器来解决这个问题。sparse-checkoutpartial clone 则从另一个角度优化——只下载需要的部分

一句话理解

LFS 解决「大文件」问题,sparse-checkout 解决「大仓库(文件多)」问题,partial clone 解决「历史太深」问题。


Part 1: Git LFS

核心概念

bash
# LFS 的工作原理:
# 1. 大文件不直接存在 Git 对象中
# 2. Git 仓库中只存一个「指针文件」(~130 字节)
# 3. 实际文件存储在 LFS 服务器上
# 4. checkout 时自动下载需要的文件

# 指针文件的内容:
# version https://git-lfs.github.com/spec/v1
# oid sha256:abc1234def5678...
# size 52428800

安装与配置

bash
# 安装
# Windows (Scoop)
scoop install git-lfs

# Windows (Git for Windows 2.x+ 自带)
# 无需额外安装

# macOS
brew install git-lfs

# Linux (Ubuntu/Debian)
sudo apt install git-lfs

# 初始化(每台机器只需运行一次)
git lfs install
# Updated git hooks.
# Git LFS initialized.

基本用法

跟踪大文件

bash
# 跟踪特定扩展名
git lfs track "*.psd"
git lfs track "*.mp4"
git lfs track "*.zip"

# 跟踪特定目录下的文件
git lfs track "assets/videos/**"
git lfs track "models/*.bin"

# 跟踪特定文件
git lfs track "data/large-dataset.csv"

# 查看当前跟踪规则
git lfs track
# Listing tracked patterns
#     *.psd (.gitattributes)
#     *.mp4 (.gitattributes)
#     assets/videos/** (.gitattributes)

# 取消跟踪
git lfs untrack "*.psd"

# ⚠️ 必须提交 .gitattributes
git add .gitattributes
git commit -m "chore: 配置 Git LFS 跟踪规则"

日常操作

bash
# LFS 文件的日常操作和普通文件完全一样
git add assets/design.psd
git commit -m "feat: 添加设计稿"
git push origin main
# Git 会自动将 LFS 文件上传到 LFS 服务器

# 查看 LFS 文件状态
git lfs status
# Objects to be pushed to origin/main:
#   assets/design.psd (LFS: abc1234)

# 查看 LFS 管理的文件列表
git lfs ls-files
# abc1234 * assets/design.psd
# def5678 * assets/video.mp4

# 查看 LFS 文件信息
git lfs pointer --file=assets/design.psd

克隆与拉取

bash
# 正常克隆(自动下载 LFS 文件)
git clone https://github.com/team/project.git

# 跳过 LFS 文件(只下载指针文件)
GIT_LFS_SKIP_SMUDGE=1 git clone https://github.com/team/project.git

# 之后按需拉取
git lfs pull
# 或只拉取特定文件
git lfs pull --include="assets/needed-file.psd"
git lfs pull --include="*.mp4" --exclude="archive/*"

# 获取但不替换工作区文件(预下载)
git lfs fetch
git lfs fetch --recent        # 只获取最近引用的文件
git lfs fetch --all           # 获取所有版本的 LFS 文件

LFS 高级用法

文件锁定

bash
# LFS 支持文件锁定(防止二进制文件的合并冲突)
# 二进制文件无法 merge,所以需要锁定机制

# 锁定文件(其他人无法修改)
git lfs lock assets/design.psd
# Locked assets/design.psd

# 查看已锁定的文件
git lfs locks
# ID    Path                  Owner    Locked At
# 1     assets/design.psd     Alice    2026-03-08T10:00:00Z

# 解锁文件
git lfs unlock assets/design.psd

# 强制解锁(管理员,释放他人的锁)
git lfs unlock assets/design.psd --force

# 在 .gitattributes 中标记需要锁定的文件类型
# *.psd filter=lfs diff=lfs merge=lfs -text lockable
# 设置 lockable 后,checkout 出的文件默认只读
# 必须先 lock 才能编辑

自定义 LFS 服务器

bash
# 默认使用 Git 托管平台的 LFS 服务(GitHub、GitLab 等)

# 为特定仓库指定 LFS 服务器
git config lfs.url https://lfs.company.com/team/repo

# 或在 .lfsconfig 中配置(提交到仓库,团队共享)
cat > .lfsconfig << 'EOF'
[lfs]
    url = https://lfs.company.com/team/repo
EOF
git add .lfsconfig
git commit -m "chore: 配置自定义 LFS 服务器"

迁移现有仓库到 LFS

bash
# 将已经在 Git 历史中的大文件迁移到 LFS

# 1. 查看哪些文件应该用 LFS
git lfs migrate info
# migrate: Fetching remote refs: ..., done.
# migrate: Sorting commits: ..., done.
# migrate: Examining commits: 100% (500/500), done.
# *.psd   50 MB   5 files
# *.mp4   200 MB  10 files

# 指定文件大小阈值
git lfs migrate info --above=5mb

# 2. 执行迁移(会重写历史!)
git lfs migrate import --include="*.psd,*.mp4"
# migrate: Fetching remote refs: ..., done.
# migrate: Sorting commits: ..., done.
# migrate: Rewriting commits: 100% (500/500), done.
#   main    abc1234 -> def5678
# migrate: Updating refs: ..., done.

# 只迁移特定分支
git lfs migrate import --include="*.psd" --include-ref=main

# 3. 强制推送(历史已重写)
git push --force --all
git push --force --tags

# 4. 通知团队重新克隆

LFS 存储管理

bash
# 查看 LFS 存储使用量
git lfs env
# Endpoint=https://github.com/team/repo.git/info/lfs (auth=basic)
# LocalMediaDir=/path/to/.git/lfs/objects
# ...

# 清理本地 LFS 缓存
git lfs prune
# prune: 42 local objects, 30 retained, done.
# prune: Deleting objects: 100% (12/12), done.

# 干跑模式
git lfs prune --dry-run

# 验证 LFS 文件完整性
git lfs fsck

# 查看 LFS 对象的本地缓存
ls .git/lfs/objects/

LFS 与各平台的配额

平台免费存储免费带宽付费方案
GitHub1 GB1 GB/月$5/50GB 存储+带宽
GitLab5 GB无限按套餐
Bitbucket1 GB5 GB/月按套餐
Azure DevOps无限无限免费
bash
# GitHub 上查看 LFS 使用量
# Settings → Billing → Git LFS Data

# 优化 LFS 带宽
# 1. CI 中跳过 LFS
GIT_LFS_SKIP_SMUDGE=1 git clone <url>

# 2. 只获取需要的文件
git lfs fetch --include="src/assets/icons/*"

# 3. 浅克隆减少历史版本下载
git clone --depth 1 <url>

Part 2: sparse-checkout(稀疏检出)

核心概念

bash
# 问题:monorepo 有 10 万个文件,但你只需要 frontend/ 目录
# 解决:sparse-checkout 只检出你需要的目录

# 没有 sparse-checkout:
# clone → 下载所有文件 → 工作区 10 万个文件 → IDE 卡死

# 使用 sparse-checkout:
# clone → 下载所有 Git 对象(历史)→ 只检出指定目录 → 工作区 500 个文件

基本用法

bash
# 初始化 sparse-checkout
git sparse-checkout init

# 设置要检出的目录(cone 模式,推荐)
git sparse-checkout init --cone
git sparse-checkout set frontend/ shared/ docs/

# 查看当前配置
git sparse-checkout list
# frontend/
# shared/
# docs/

# 添加更多目录
git sparse-checkout add backend/api/

# 取消 sparse-checkout(检出所有文件)
git sparse-checkout disable

从零开始的 sparse clone

bash
# 1. 克隆时不检出文件
git clone --no-checkout https://github.com/team/monorepo.git
cd monorepo

# 2. 初始化 sparse-checkout
git sparse-checkout init --cone

# 3. 设置要检出的目录
git sparse-checkout set packages/frontend packages/shared

# 4. 检出文件
git checkout main

# 现在工作区只有 packages/frontend/ 和 packages/shared/

配合 partial clone

bash
# 终极优化:partial clone + sparse-checkout
# 不下载不需要的 blob 对象 + 不检出不需要的文件

git clone \
  --filter=blob:none \
  --sparse \
  https://github.com/team/monorepo.git

cd monorepo
git sparse-checkout set packages/frontend packages/shared

# 效果:
# - 克隆速度极快(只下载树对象和提交对象)
# - 工作区只有需要的文件
# - 访问其他文件时按需下载

sparse-checkout 模式

Cone 模式(推荐)

bash
# Cone 模式:基于目录的包含/排除
git sparse-checkout init --cone

# 只能指定目录(不能指定单个文件的模式)
git sparse-checkout set src/ docs/
# 根目录的文件 + src/ + docs/ 会被检出

# 优势:
# - 性能更好(O(1) 匹配而非 O(n))
# - 规则更直观
# - Git 2.37+ 默认模式

Non-cone 模式

bash
# Non-cone 模式:支持 gitignore 风格的模式
git sparse-checkout init --no-cone

# 可以使用通配符
git sparse-checkout set \
  "/*" \
  "!/packages/*" \
  "/packages/frontend/" \
  "/packages/shared/"

# 或编辑配置文件
# .git/info/sparse-checkout

Part 3: Partial Clone(部分克隆)

克隆过滤器

bash
# 1. 无 blob 克隆(最常用)
git clone --filter=blob:none <url>
# 不下载任何文件内容,只下载提交和树对象
# 访问文件时按需下载
# 适合:大型仓库的快速克隆

# 2. 按大小过滤
git clone --filter=blob:limit=1m <url>
# 不下载超过 1MB 的文件
# 适合:仓库中有少量大文件

# 3. 无树克隆(实验性)
git clone --filter=tree:0 <url>
# 不下载任何树对象
# 适合:只需要最新快照

# 4. 浅克隆
git clone --depth 1 <url>
# 只下载最近 1 个提交
# 适合:CI/CD 环境

# 5. 单分支克隆
git clone --single-branch --branch main <url>
# 只克隆指定分支

组合使用

bash
# CI/CD 最优克隆策略
git clone \
  --depth 1 \
  --single-branch \
  --branch main \
  --filter=blob:none \
  <url>

# 开发者日常使用
git clone \
  --filter=blob:none \
  --sparse \
  <url>
git sparse-checkout set <my-package>

# 浅克隆后获取更多历史
git fetch --unshallow
# 或获取指定深度
git fetch --depth 100

部分克隆的内部机制

bash
# 部分克隆使用 promisor remote
# Git 知道某些对象虽然本地没有,但可以从远程获取

# 查看本地缺失的对象
git rev-list --objects --all --missing=print | grep "^?"

# 手动获取缺失的对象
git fetch origin --filter=blob:none

Part 4: 大仓库优化策略

决策流程

性能配置

bash
# 文件系统监视器(显著加速 status/diff)
git config core.fsmonitor true           # Git 2.37+
git config core.untrackedcache true

# 提交图缓存(加速 log/merge-base/rebase)
git config fetch.writeCommitGraph true
git commit-graph write --reachable

# 多线程操作
git config pack.threads 0                # 自动使用所有核心
git config checkout.workers 0            # 并行检出
git config index.threads true            # 并行索引

# 后台维护
git maintenance start

# 打包优化
git config pack.windowMemory 256m        # 限制内存使用
git config pack.packSizeLimit 2g         # 限制单个 packfile 大小

# 预取(后台提前获取远程数据)
git config fetch.negotiationAlgorithm skipping  # 更快的协商算法

不同场景的推荐方案

场景推荐方案
设计资源(PSD/AI/视频)Git LFS
ML 模型/数据集Git LFS 或 DVC
Monorepo(前端+后端+...)sparse-checkout + partial clone
CI/CD 构建--depth 1 --single-branch
历史中有废弃的大文件git filter-repo --strip-blobs-bigger-than 10M
仓库已经很大了迁移大文件到 LFS + filter-repo 清理历史
游戏开发(大量二进制)Git LFS + 文件锁定

实战场景

场景 1:游戏开发项目配置

bash
# .gitattributes — 游戏项目
# 3D 模型
*.fbx filter=lfs diff=lfs merge=lfs -text lockable
*.obj filter=lfs diff=lfs merge=lfs -text
*.blend filter=lfs diff=lfs merge=lfs -text lockable

# 贴图
*.png filter=lfs diff=lfs merge=lfs -text
*.tga filter=lfs diff=lfs merge=lfs -text
*.psd filter=lfs diff=lfs merge=lfs -text lockable

# 音频
*.wav filter=lfs diff=lfs merge=lfs -text
*.mp3 filter=lfs diff=lfs merge=lfs -text
*.ogg filter=lfs diff=lfs merge=lfs -text

# 视频
*.mp4 filter=lfs diff=lfs merge=lfs -text

# 编译产物
*.dll filter=lfs diff=lfs merge=lfs -text
*.so filter=lfs diff=lfs merge=lfs -text

# 场景/关卡文件
*.unity filter=lfs diff=lfs merge=lfs -text lockable
*.umap filter=lfs diff=lfs merge=lfs -text lockable

场景 2:Monorepo 开发者工作流

bash
# 团队有一个大型 monorepo,每个开发者只负责部分代码

# 前端开发者
git clone --filter=blob:none --sparse https://github.com/team/monorepo.git
cd monorepo
git sparse-checkout set packages/web packages/shared configs/

# 后端开发者
git clone --filter=blob:none --sparse https://github.com/team/monorepo.git
cd monorepo
git sparse-checkout set packages/api packages/shared configs/

# 需要看其他包时临时添加
git sparse-checkout add packages/mobile

# 不需要了再移除
git sparse-checkout set packages/api packages/shared configs/

场景 3:CI/CD 中的 LFS 优化

yaml
# GitHub Actions — 优化 LFS 使用

# 方案 1:跳过 LFS(如果构建不需要大文件)
- uses: actions/checkout@v4
  with:
    lfs: false

# 方案 2:缓存 LFS 文件
- uses: actions/checkout@v4
  with:
    lfs: true
- name: Cache LFS objects
  uses: actions/cache@v4
  with:
    path: .git/lfs
    key: lfs-${{ hashFiles('.gitattributes') }}

# 方案 3:只获取需要的 LFS 文件
- uses: actions/checkout@v4
  with:
    lfs: false
- name: Fetch specific LFS files
  run: |
    git lfs fetch --include="assets/icons/*"
    git lfs checkout

场景 4:从 Git 到 LFS 的完整迁移

bash
#!/bin/bash
# migrate-to-lfs.sh — 将现有仓库迁移到 LFS

echo "🔍 分析仓库中的大文件..."
git lfs migrate info --above=5mb --top=20

echo ""
read -p "确认迁移?这会重写历史!(y/N): " CONFIRM
if [ "$CONFIRM" != "y" ]; then
    echo "取消迁移"
    exit 0
fi

echo ""
echo "📦 执行迁移..."
git lfs migrate import \
    --include="*.psd,*.ai,*.sketch" \
    --include="*.mp4,*.mov,*.avi" \
    --include="*.zip,*.tar.gz" \
    --include="*.bin,*.dat,*.model"

echo ""
echo "🧹 清理..."
git reflog expire --expire=now --all
git gc --prune=now --aggressive

echo ""
echo "📊 迁移后仓库大小:"
git count-objects -vH

echo ""
echo "⚠️ 下一步操作:"
echo "  1. git push --force --all"
echo "  2. git push --force --tags"
echo "  3. 通知团队重新克隆"
echo "  4. 在 GitHub/GitLab 上清理服务端缓存"

常见陷阱

陷阱 1:忘记提交 .gitattributes
bash
# ❌ track 了 LFS 但没有提交 .gitattributes
git lfs track "*.psd"
git add assets/design.psd
git commit -m "add design"
# 文件被当作普通文件提交了!

# ✅ 先提交 .gitattributes
git lfs track "*.psd"
git add .gitattributes
git commit -m "chore: 配置 LFS"
git add assets/design.psd
git commit -m "feat: 添加设计稿"
陷阱 2:LFS 配额耗尽
bash
# GitHub 免费只有 1GB 存储 + 1GB/月带宽
# 频繁 CI clone 会快速消耗带宽

# ✅ 解决方案:
# 1. CI 中跳过 LFS
GIT_LFS_SKIP_SMUDGE=1 git clone <url>
# 2. 缓存 LFS 对象
# 3. 使用自建 LFS 服务器
# 4. 定期清理旧版本 LFS 文件
陷阱 3:sparse-checkout 后误删文件
bash
# sparse-checkout 隐藏的文件不在工作区中
# 但 Git 仍然跟踪它们!

# ❌ 以为文件不存在就创建同名文件
# 可能导致冲突

# ✅ 查看完整的文件列表
git ls-tree -r HEAD --name-only
陷阱 4:LFS 文件无法 diff/merge
bash
# 二进制文件(PSD、MP4)无法 diff
# 两个人同时修改同一个 PSD 会冲突且无法自动合并

# ✅ 使用文件锁定
git lfs lock assets/design.psd
# 编辑...
git add assets/design.psd
git commit -m "update design"
git lfs unlock assets/design.psd
陷阱 5:浅克隆的限制
bash
# 浅克隆不能:
# - git log(只能看到有限的历史)
# - git blame(可能不完整)
# - git bisect(范围受限)
# - push 到新仓库(可能被拒绝)

# ✅ 需要完整历史时
git fetch --unshallow

常见问题

LFS 和 .gitattributes 是什么关系?

git lfs track 命令实际上是在 .gitattributes 中添加规则。LFS 通过 Git 的 filter 属性实现——clean 过滤器在 add 时将文件替换为指针,smudge 过滤器在 checkout 时将指针替换为实际文件。

partial clone 和浅克隆有什么区别?
特性浅克隆 --depth部分克隆 --filter
限制内容历史深度对象类型/大小
按需获取需要 --unshallow自动按需获取
适用场景CI/CD、快速查看日常开发
Git 版本一直支持Git 2.22+
sparse-checkout 会影响 git status 吗?

不会。git status 只显示已检出的文件的状态。未检出的文件既不会显示为已修改也不会显示为已删除。

如何查看 LFS 实际使用了多少存储?
bash
# 本地 LFS 缓存大小
du -sh .git/lfs/

# GitHub 上查看
# Settings → Billing → Git LFS Data

# 清理本地缓存
git lfs prune
Windows 上 LFS 性能问题?
bash
# Windows 上 LFS 可能较慢,优化方法:
git config lfs.concurrenttransfers 8       # 增加并行下载数
git config lfs.transfer.maxretries 5       # 增加重试次数
git config core.longpaths true             # 支持长路径

速查表

Git LFS

目标命令
安装 LFSgit lfs install
跟踪文件类型git lfs track "*.psd"
查看跟踪规则git lfs track
取消跟踪git lfs untrack "*.psd"
查看 LFS 文件列表git lfs ls-files
查看 LFS 状态git lfs status
拉取 LFS 文件git lfs pull
按需拉取git lfs pull --include="*.psd"
跳过 LFS 克隆GIT_LFS_SKIP_SMUDGE=1 git clone <url>
锁定文件git lfs lock <file>
解锁文件git lfs unlock <file>
迁移到 LFSgit lfs migrate import --include="*.psd"
清理本地缓存git lfs prune
验证完整性git lfs fsck

sparse-checkout

目标命令
初始化git sparse-checkout init --cone
设置检出目录git sparse-checkout set dir1/ dir2/
添加目录git sparse-checkout add dir3/
查看配置git sparse-checkout list
禁用git sparse-checkout disable

partial clone

目标命令
无 blob 克隆git clone --filter=blob:none <url>
按大小过滤git clone --filter=blob:limit=1m <url>
浅克隆git clone --depth 1 <url>
单分支克隆git clone --single-branch -b main <url>
组合优化git clone --filter=blob:none --sparse <url>
获取完整历史git fetch --unshallow

相关命令

参考资料