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 目录中完整保存该文件的所有版本。这会导致:
-
仓库体积爆炸:导致克隆(Clone)耗时极长。
-
内存溢出:在处理巨型二进制文件时,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 是为了优化大文件的存储。
| 特性 | 标准 Git | Git 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 版本
bash
git -v
验证当前 git 是否内置 Git LFS
bash
git lfs version
4.2:全局初始化
安装后,需要运行初始化命令以配置全局 Git 钩子(Hooks)。每台机器仅需初始化一次。
bash
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,不影响其他项目。 |
验证是否已初始化
这三条命令都可以验证
bash
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 配置是什么
bash
git config --get-regexp filter.lfs
只要看到这个 clean、smudge、process 等内容就说明初始化成功
text
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 你想管理哪些类型的文件,例如:
bash
git lfs track "*.psd"
git lfs track "*.zip"
git lfs track "*.mp4"
git lfs track "*.fbx"
git lfs track "*.obj"
📌执行后,项目根目录会生成(或更新)一个 .gitattributes 文件。
❗这个文件必须提交到 Git 仓库,否则他人拉取代码无法生效
bash
git add .gitattributes
git commit -m "chore: 配置git-lfs跟踪规则"
4.4:查看追踪状态
查看当前有哪些文件正在通过 LFS 进行管理
bash
git lfs ls-files
4.5:查看分发状态
查看当前工作区大文件的具体分发状态
bash
git lfs status
4.5:git 和 git-lfs 对于代码的操作方式有区别吗?
👉在日常操作命令上,Git 和 Git LFS 是完全一样的。
-
提交(Push)
命令: 依然是 git add -> git commit -> git push。
底层区别: 当你执行 git push 时,Git 发现某个文件被 LFS 追踪了,它会先通过 HTTPS 把大文件扔给 LFS 服务器,然后再把"指针文件"传给 Git 仓库。
-
拉取(Pull)
命令: 依然是 git pull。
底层区别: 当你执行 git pull 时,Git 下载了指针文件,LFS 钩子会自动识别并去 LFS 服务器把真文件下载下来替换掉指针。
注意:如果你克隆了一个仓库,发现大文件只是几行的文本指针(通常只有几百字节),可以使用以下命令手动补全:
bashgit lfs pull -
合并(Merge)
命令: 依然是 git merge。
难点: 如果两个人在同一个大文件(如一张图片)上做了修改并合并,Git 无法像合并代码那样进行"行对比"。它会产生冲突,你需要用
git checkout --ours或git checkout --theirs来选择保留哪个二进制版本。
只有在初始配置时,需要额外的Git LFS特定命令,如 git lfs install(初始化)和 git lfs track(指定跟踪模式),但这些是一次性的或偶尔的操作,不会影响日常的提交、拉取或合并。
5. 迁移现有文件到 LFS
5.1 迁移前的核心准备
-
备份仓库:该操作会重写历史(改变所有旧提交的哈希值)。在开始之前,请务必克隆一份备份。
-
清理工作区:确保当前没有未提交的改动(git status 应当是干净的)。
-
通知团队:由于 Commit ID 会全变,团队成员在迁移完成后不能直接 git pull,必须重新 clone 或执行复杂的重置操作。
5.2 分析历史
在动手转换之前,先看看历史记录里到底哪些文件最占空间:
bash
git lfs migrate info --everything
作用:扫描所有分支,列出占用空间最大的文件类型。
输出:你会看到类似 *.mp4 (500 MB) 这样的统计信息,这能帮你决定该迁移哪些后缀。
5.3 执行迁移
你可以根据需求选择"按后缀迁移"或"按文件大小迁移"。
-
按文件后缀迁移(最常用)
如果你想把项目历史中所有的视频和压缩包转为 LFS:
bashgit lfs migrate import --include="*.mp4,*.zip,*.mov" --everythingimport:执行从 Git 到 LFS 的导入。
--include:指定匹配模式,支持逗号分隔。
--everything:关键参数。如果不加,只处理当前分支;加上后会处理本地所有的 branches 和 tags。
-
将特定的巨型文件转为 LFS
如果你只想针对某个特定的大文件:
bashgit lfs migrate import --include="path/to/large_dataset.bin" --everything
5.4 物理清理(瘦身)
运行完 migrate 后,你会发现 .git 文件夹体积并没有立刻减小。这是因为旧的对象还缓存在 Git 的重写记录(Reflog)中。
执行以下命令进行"物理切除":
bash
# 1. 移除所有旧的引用记录
git reflog expire --expire=now --all
# 2. 彻底清理并压缩数据库
git gc --prune=now --aggressive
此时,你会发现 .git 文件夹显著变小,而 .git/lfs/objects 下出现了对应的大文件。
5.5 同步到远程(强制推送)
由于你改变了历史,直接 push 会被拒绝。你需要"暴力"更新远程仓库:
bash
# 推送所有分支
git push origin --force --all
# 推送所有标签
git push origin --force --tags
5.6 迁移后的验证
检查配置文件:查看根目录是否多了一个 .gitattributes,里面应包含你刚才指定的后缀。
列出现在由 LFS 管理的文件:
bash
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进行管理