Git LFS 扫盲教程 - 你不会还在用 Git 管理大文件吧?

30 阅读10分钟

1. Git LFS 是什么?

Git LFS (Large File Storage) 是官方开源的一个 Git 扩展,用于高效管理大型二进制文件,比如:

  • 视频 / 音频

  • 模型(.fbx、.obj、.pt、.onnx)

  • AI / ML 模型

  • Photoshop / Blender / UE / Unity 资源

  • 超过几十 MB 的二进制文件

它将 Git 仓库中的大型文件替换为 指针文件(pointer files),并将实际文件内容存储在远程服务器(GitHub,Gitee,GitLab等)。只在需要时从远程服务器下载实际文件内容。这有助于保持 Git 仓库的体积小巧,避免标准 Git 仓库因大型文件而膨胀

它解决了 Git 在处理大文件时由于全量备份机制导致的仓库体积膨胀导致的克隆、推送和历史记录操作变慢,以及单文件大小超过 100MB 无法提交到仓库等问题

Git LFS 不替代 Git,仅扩展其能力。工作流与标准 Git 相同,日常代码的拉取合并推送等操作与标准 Git 完全一致。 LFS 在后台处理大型文件。

官方文档:git-lfs.com/

2. 为什么需要 Git LFS?

  • 标准 Git 的局限性:

    传统的 Git 在设计之初是为文本文件(代码)优化的。由于 Git 采用“全量快照”机制,Git 将所有文件(包括大型二进制文件)存储在 .git 目录中,每个版本变更都会完整复制文件,导致仓库体积快速增长。克隆仓库时,需要下载所有历史版本的文件。

    每当提交一个大文件(如 100MB 的视频)的微小修改,Git 都会在 .git 目录中完整保存该文件的所有版本。这会导致:

    1. 仓库体积爆炸:导致克隆(Clone)耗时极长。

    2. 内存溢出:在处理巨型二进制文件时,Git 性能显著下降。

  • Git LFS 的优化:

    它在 Git 仓库中仅存储一个几百字节的指针文件,而将真实的二进制内容存储在专门的 LFS 服务器上。

    大型文件被转换为指针文件(通常几百字节大小,包含文件元数据如 SHA-256 哈希、版本和远程存储 URL)。

    实际文件内容上传到支持 LFS 的远程服务器(如 GitHub、GitLab 或 Bitbucket 的 LFS 端点)。 Git 操作(如 git checkout)时,通过 Git 的 smudge/clean 过滤器自动从远程下载/上传文件内容。

2.1 Git LFS 与标准 Git 的区别

📌Git 本质上是为文本文件(代码)设计的,而 Git LFS 是为了优化大文件的存储。

特性标准 GitGit LFS
存储方式将文件的所有版本内容完整保存在本地 .git 目录中本地只保存一个几百字节的指针文件,真实内容存在远程 LFS 服务器。
克隆速度文件越多/越大,克隆越慢(因为要下载全历史)极快(只下载你当前检出版本所需的大文件)
文件大小限制建议不超过 50-100MB,否则性能大幅下降可以轻松处理 GB 级别的文件
数据同步git push/pull 同步所有数据仅在需要时通过 HTTPS 传输大文件

2.2 为什么标准 Git 不适合大文件?

2.2.1 Git 的设计初衷

Git 适合管理:

  • 文本文件

  • 体积小、可 diff 的内容

  • 高频提交、分支、合并

Git 不适合管理:

  • 大体积二进制文件(模型、视频、压缩包)

  • 频繁变更的大文件

2.2.2 Git 遇到大文件的问题

问题原因
仓库体积爆炸Git 每次提交都会保存完整版本
clone 超慢所有历史版本都会被拉下来
diff / merge 没意义二进制文件无法行级 diff
GitHub 限制GitHub 单文件 ≤ 100MB

💥 哪怕你删了大文件,Git 历史里仍然存在

3. 同一个项目中 Git LFS 和 Git 可以混用吗?

完全可以,而且这正是它的用法

用 Git LFS 专门跟踪和管理大型文件,而其他小文件(如代码、文本)继续用标准 Git 处理。整个工作流程保持一致,不需要切换工具。

你只需要告诉 LFS 哪些后缀的文件需要它来接管。

4. Git LFS 使用教程

4.1:安装组件

Git for Windows v2.7.0 (2016年) 起,已经集成了 Git LFS 组件。无需单独下载

查看当前 git 版本

git -v

验证当前 git 是否内置 Git LFS

git lfs version

4.2:全局初始化

安装后,需要运行初始化命令以配置全局 Git 钩子(Hooks)。每台机器仅需初始化一次。

git lfs install

该命令会将 filter.lfs 配置写入你的 ~/.gitconfig 文件。你可以通过 git config --global -list 验证

默认会带上 --global,所以等同于 git lfs install --global,详情见下表

命令影响范围说明
git lfs install全局 (User)最推荐用法。修改 ~/.gitconfig,对当前用户所有项目生效。
git lfs install --global全局 (User)与上一条命令完全等效,显式指定全局标志。
git lfs install --system全系统 (System)修改系统级配置,对电脑上所有用户生效(需管理员权限)。
git lfs install --local当前项目 (Repo)仅修改当前仓库的 .git/config,不影响其他项目。

验证是否已初始化

这三条命令都可以验证

git config --local -l

git config --global -l

git config --system -l

末尾的 l 是 list 的简写,简写或者全写效果是一样的

可能存在 local 未初始化 LFS, 但是 global 已初始化。或者 local 和 global 都没初始化,但 system 已初始化的情况

Git 的配置遵循 “就近原则”(优先级:Local > Global > System

只要这三层中任意一层包含了 LFS 的 filter 配置,Git 就能识别并处理大文件

你也可以不用管它哪一层已初始化了 LFS,直接运行命令查看最终生效的 LFS 配置是什么

git config --get-regexp filter.lfs

只要看到这个 clean、smudge、process 等内容就说明初始化成功

filter.lfs.clean=git-lfs clean -- %f
filter.lfs.smudge=git-lfs smudge -- %f
filter.lfs.process=git-lfs filter-process
filter.lfs.required true

题外话:可能是现在的 Git for Windows,在安装过程中,默认会勾选 "Git LFS (Large File Storage)" 组件。或者 Sourcetree 在第一次打开带 LFS 的仓库时,会弹窗问你“是否初始化”,点击“是”它就会帮你写好配置。或者某些 IDE 插件:比如 VS Code 的 GitLens 或一些重量级的工程脚手架,为了环境对齐,会自动检测并补齐这些配置。总之就是,我并没有运行过 lfs 初始化命令,可我运行 git config --system -l 却发现系统中 lfs 已经初始化了,算鸟无所谓,不去纠结这些小细节,既然已经成功初始化,那我直接使用即可

4.3:追踪大文件:指定哪些文件用 LFS 管理(核心操作)

告诉 LFS 你想管理哪些类型的文件,例如:

git lfs track "*.psd"
git lfs track "*.zip"
git lfs track "*.mp4"
git lfs track "*.fbx"
git lfs track "*.obj"

📌执行后,项目根目录会生成(或更新)一个 .gitattributes 文件。

这个文件必须提交到 Git 仓库,否则他人拉取代码无法生效

git add .gitattributes
git commit -m "chore: 配置git-lfs跟踪规则"

4.4:查看追踪状态

查看当前有哪些文件正在通过 LFS 进行管理

git lfs ls-files

4.5:查看分发状态

查看当前工作区大文件的具体分发状态

git lfs status

4.5:git 和 git-lfs 对于代码的操作方式有区别吗?

👉在日常操作命令上,Git 和 Git LFS 是完全一样的。

  1. 提交(Push)

    命令: 依然是 git add -> git commit -> git push。

    底层区别: 当你执行 git push 时,Git 发现某个文件被 LFS 追踪了,它会先通过 HTTPS 把大文件扔给 LFS 服务器,然后再把“指针文件”传给 Git 仓库。

  2. 拉取(Pull)

    命令: 依然是 git pull。

    底层区别: 当你执行 git pull 时,Git 下载了指针文件,LFS 钩子会自动识别并去 LFS 服务器把真文件下载下来替换掉指针。

    注意:如果你克隆了一个仓库,发现大文件只是几行的文本指针(通常只有几百字节),可以使用以下命令手动补全:

    git lfs pull
    
  3. 合并(Merge)

    命令: 依然是 git merge。

    难点: 如果两个人在同一个大文件(如一张图片)上做了修改并合并,Git 无法像合并代码那样进行“行对比”。它会产生冲突,你需要用 git checkout --oursgit checkout --theirs 来选择保留哪个二进制版本。

只有在初始配置时,需要额外的Git LFS特定命令,如 git lfs install(初始化)和 git lfs track(指定跟踪模式),但这些是一次性的或偶尔的操作,不会影响日常的提交、拉取或合并。

5. 迁移现有文件到 LFS

5.1 迁移前的核心准备

  • 备份仓库:该操作会重写历史(改变所有旧提交的哈希值)。在开始之前,请务必克隆一份备份。

  • 清理工作区:确保当前没有未提交的改动(git status 应当是干净的)。

  • 通知团队:由于 Commit ID 会全变,团队成员在迁移完成后不能直接 git pull,必须重新 clone 或执行复杂的重置操作。

5.2 分析历史

在动手转换之前,先看看历史记录里到底哪些文件最占空间:

git lfs migrate info --everything

作用:扫描所有分支,列出占用空间最大的文件类型。

输出:你会看到类似 *.mp4 (500 MB) 这样的统计信息,这能帮你决定该迁移哪些后缀。

5.3 执行迁移

你可以根据需求选择“按后缀迁移”或“按文件大小迁移”。

  1. 按文件后缀迁移(最常用)

    如果你想把项目历史中所有的视频和压缩包转为 LFS:

    git lfs migrate import --include="*.mp4,*.zip,*.mov" --everything
    

    import:执行从 Git 到 LFS 的导入。

    --include:指定匹配模式,支持逗号分隔。

    --everything:关键参数。如果不加,只处理当前分支;加上后会处理本地所有的 branches 和 tags。

  2. 将特定的巨型文件转为 LFS

    如果你只想针对某个特定的大文件:

    git lfs migrate import --include="path/to/large_dataset.bin" --everything
    

5.4 物理清理(瘦身)

运行完 migrate 后,你会发现 .git 文件夹体积并没有立刻减小。这是因为旧的对象还缓存在 Git 的重写记录(Reflog)中。

执行以下命令进行“物理切除”:

# 1. 移除所有旧的引用记录
git reflog expire --expire=now --all

# 2. 彻底清理并压缩数据库
git gc --prune=now --aggressive

此时,你会发现 .git 文件夹显著变小,而 .git/lfs/objects 下出现了对应的大文件。

5.5 同步到远程(强制推送)

由于你改变了历史,直接 push 会被拒绝。你需要“暴力”更新远程仓库:

# 推送所有分支
git push origin --force --all

# 推送所有标签
git push origin --force --tags

5.6 迁移后的验证

检查配置文件:查看根目录是否多了一个 .gitattributes,里面应包含你刚才指定的后缀。

列出现在由 LFS 管理的文件:

git lfs ls-files

你应该能看到历史版本中的文件现在都显示为 LFS 追踪状态。

你应该能看到大文件的路径,以及它对应的 LFS 哈希值(而不是 Git 的哈希值)。

迁移完成后,建议在团队内部约好一个时间点,所有人统一更新。直接删除旧目录并重新克隆是最干净的选择

6. ⚠️ Git LFS 注意事项

  • 尽早追踪: 最好在大文件还没提交到仓库之前就执行 git lfs track。如果文件已经作为普通 Git 文件提交过了,它的历史记录依然会占用空间,如果你已经用普通 git 提交过了大文件,可以用 git lfs migrate 或者重新建仓库解决

  • 不要追踪代码: 千万不要用 LFS 追踪 .c 或 .java 等小文件,这会让你在离线工作时无法查看历史版本

  • 关注配额: GitHub免费提供 1GB 存储/月带宽,超出需要付费或用自建服务器

  • 提交.gitattributes:不要忘记提交到仓库,否则其他人 clone 后会将大文件通过普通git进行管理