Git LFS 大文件存储
文档用途
介绍 Git LFS 的使用场景和方法, 并说明如何为既有项目做迁移.
LFS 是 Large File Storage 的缩写, 即大文件存储.
参考链接
官方资料: Git LFS 官网 和 Git LFS 的 GitHub 主页.
知乎文章: 详解 Git 大文件存储 (Git LFS).
Why Git LFS
文本文件
Git 非常适合对文本文件做版本控制:
- 当文件发生修改时, Git 会记录 "在哪儿发生了怎样的修改",
而不是直接记录新文件, 因此记录每次修改只需要非常小的存储空间. - Git 为了加快网络传输速度, 在
push前会对内容做压缩,fetch到本地之后也会解压缩.
而文本文件的压缩/解压缩速度快且压缩率高, 同时压缩会让文件数减少, 提升 IO 效率.
资源文件
当资源文件 (图片, 视频, 音频, 二进制文件等) 被修改时, 问题就来了:
- 对资源文件很小的改动, 很可能会以非常大的改动体现在其对应的文本内容上,
因此对资源文件的每次改动都很可能会让其所需的存储空间快速增长, 几乎相当于多次复制. - 资源文件的压缩速度慢, 压缩率低, 如果不压缩直接被网络传输速度反而还快一些.
- 对于大体积的资源文件 (特别是它们还经常被修改) 的项目,
初始克隆需要大量时间, 因为客户端会下载每个文件的每个版本.
Git LFS 的原理
Git LFS 通过懒惰地 (lazily) 下载大文件的相关版本来减少大文件在仓库中的影响.
具体来说, 大文件是在 checkout 的过程中下载的, 而不是 clone 或 fetch 过程中下载的.
Git LFS 通过将仓库中的大文件替换为小于 1 KB 的指针文件来做到这一点.
在正常使用期间, 我们不会看到这些指针文件, 因为它们是由 Git LFS 自动处理的:
- 当我们执行
git add命令时, Git LFS 用一个指针替换实际内容,
并将实际内容存储在本地 LFS 缓存中 (本地缓存位于仓库的 .git/lfs/objects 路径下). git push时新增的 commit 所引用的所有大文件都会从本地传输到远程仓库的远程存储.- 当我们
checkout一个包含 Git LFS 指针的 commit 时 (pull包含了checkout命令),
指针文件将替换为本地 Git LFS 缓存中的文件, 或者从远端 Git LFS 存储区下载.
Git LFS 的好处
- 对于日常使用 Git 的习惯不造成影响.
- 减小本地 Git 缓存所占用的存储空间.
- 加快
git fetch或git pull的速度. - 加快在 commit 之间切换的速度.
使用方法
本地安装
Git 官方的安装包自带 Git LFS, 可以直接在 Git 官网 下载.
也可以在 Git LFS 的官网 下载单独的安装包.
如果使用包管理工具安装 git, 一般都会附带安装 git-lfs.
scoop install gitbrew install git检查是否存在 git lfs 命令:
git lfs --version初始化工程
一条命令即可为 git 工程开启 LFS 支持:
git lfs install基础命令
# 将 .png 文件格式设置为被 Git LFS 管理的格式
git lfs track "*.png"
# 查看当前 commit 有哪些文件被认为是 LFS 文件
git lfs ls-files
# 剪除存放于本地的 LFS 文件中那些旧版本的版本, 释放一些硬盘存储空间
git lfs prune其实在大多数时候并不需要使用 git lfs 相关命令.
另外, 建议通过直接编辑 .gitattributes 文件来配置被 Git LFS 管理的文件与文件类型:
*.jpg filter=lfs diff=lfs merge=lfs -text
*.png filter=lfs diff=lfs merge=lfs -textFile Locking
Git LFS 提供了文件锁功能, 用于避免合并冲突.
如果某个开发者锁住了一个文件并做修改, 那么在此期间其他开发者就不能修改该文件.
文件锁功能需要远程仓库的支持, 详见官方文档 File Locking.
使用场景举例
避免多人同时修改 Excel 或 PSD 文件.
WARNING
如果远程仓库不支持, 可能会在 git push 时遇到以下错误:
Remote "origin" does not support the Git LFS locking API解决方案:
# 做全局配置, 禁用 git lfs 的 locksverify 功能
git config --global lfs.locksverify falseGit LFS 迁移
写在前面
当我们想到要给某个工程启用 Git LFS 时, 很有可能已经开发了一段时间.
那么就很有必要搞清楚应该如何将工程做 迁移, 使其支持 Git LFS.
如何迁移
参考 官方文档 Git LFS Migrate. 迁移前先检查所有分支内的文件:
# 把体积最大的 100 个文件列出来 (忽略已经被 LFS 管理的文件)
git lfs migrate info --everything --pointers=ignore --top=100WARNING
注意 git lfs migrate import 命令会修改 git 历史, 是个 破坏性修改.
虽然 Git 历史中各个 Commit 都还在, 但是 SHA 会发生改变.
迁移所有在 .gitattributes 列出的应该被追踪的大文件:
# 其中 --everything 代表所有分支 (包括远程仓库, 在 push 之后起效).
# 注意 --fixup 针对每个 commit 起效, 因此如果历史 commit 中没有指定 git lfs track,
# 那么这些历史 commit 就不会被正确迁移, 所以这条命令 **其实没啥用**.
git lfs migrate import --fixup --everything具体指定迁移哪些 LFS 文件, 文件类型, 并影响所有分支:
# 修改 .mp4 和 .mov 文件, --everything 代表影响所有分支
git lfs migrate import --include="*.mp4,*.mov" --everything如果只打算影响部分分支, 可以使用 --include-ref 选项:
# 修改 .mp4 文件, 影响 master 和 my-feature 分支
git lfs migrate import --include="*.mp4" \
--include-ref=refs/heads/master --include-ref=refs/heads/my-feature在实践中, 我们可能需要以下这条超长的命令, 囊括很多文件类型:
# 真正实用的迁移命令
git lfs migrate import --everything \
--include="*.jpg,*.jpeg,*.png,*.ico,*.tif,*.tiff,*.svg,*.gif,*.psd \
,*.ai,*.exr,*.hdr,*.tga,*.bmp,*.iff,*.pict,*.webp \
,*.mp3,*.wav,*.wma,*.aiff,*.ogg,*.oga,*.midi \
,*.raw,*.bank,fmodstudioL,fmodstudio,resonanceaudio \
,*.mp4,*.m4p,*.mpv,*.mpe,*.mpeg,*.mov,*.flv,*.avi,*.wmv \
,*.blend,*.fbx,*.FBX,*.obj,*.OBJ,*.abc,*.3ds,*.3DS,*.ma,*.MA \
,*.lxo,*.LXO,*.c4d,*.C4D,*.u3d,*.usd,*.usda,*.usdc \
,*.unitypackage,*.ttf,*.otf \
*.zip,*.rar,*.7z \
,*.dll,*.pbd,*.bin,*.lib,*.exe,*.aar,*.jar,*.iso,*.so,*.a,*.o,*.bc \
,*.dylib,*.pdf,*.xls,*.xlsx,*.doc,*.docx,*.ppt,*.pptx"后续工作
在迁移完成后调用以下命令, 可以将本地工程所占硬盘空间立即缩小:
git reflog expire --expire-unreachable=now --all
git gc --prune=nowINFO
就实际测试的结果来看, git lfs migrate 之后需要等几个小时再调用下面的命令才有效果.
另外, 即使不调用以上命令, 过一段时间也会被 git 自动清理.
迁移之后需要使用以下命令推送至远程仓库:
# 选项 --all 代表影响所有分支.
git push --force --allGitLab 默认对 master 分支有保护, 需要允许 force push.
打开 Settings / Repository / Protected Branches 并修改设置.

可以在 Settings / Usage Quotas 中看到远程仓库的存储空间占用情况.
例如下图是个游戏项目, 图片和音频资源作为 大文件 占用了 5.31 GB, 远远超过常规文件体积.

补充说明
LFS 文件路径
LFS 文件在 GitLab 中的默认位置:
/var/opt/gitlab/gitlab-rails/shared/lfs-objects
LFS 文件 (数据本身) 在本地工程中的位置:
.git/lfs
停止使用 LFS
首先需要说明 git lfs install 命令做了什么:
- 会给当前的工程添加一个
hook, 相当于对 git 的某些命令添加了回调. - 并且会在
~/.gitconfig文件中添加配置:
[filter "lfs"]
process = git-lfs filter-process
required = true
clean = git-lfs clean -- %f
smudge = git-lfs smudge -- %fINFO
注意这是用户层面的配置, 而不是具体的工程的配置.
hook 的文件路径
工程目录中的路径:
.git/hooks/post-checkout
.git/hooks/post-commit
.git/hooks/post-merge
.git/hooks/pre-push
如果我们不想让 Git LFS 对整个用户层面都生效, 那么我们可以这么做:
# 移除用户层面的 lfs 配置.
# 如果当前位于某个 git 工程目录的话, 也会移除这个工程的 hook.
git lfs uninstall针对具体的工程开启或关闭 Git LFS:
cd [工程目录]
git lfs install --local # 开启
git lfs uninstall --local # 关闭INFO
以上命令只会修改工程目录内的 .git/config 文件.
不会影响当前操作系统用户的 ~/.gitconfig 文件.