使用 Git LFS 存储二进制文件

 

Git LFS 简介

Git LFS 是由 Atlassian, GitHub 和其他开源贡献者开发的 Git 扩展,目的是减少大文件对 Git 仓库的影响,其以一种偷懒的方式下载相关版本的文件。具体来讲,就是只有在执行 checkout 命令时才会下载文件,而 clonefetch 均不会下载文件。

Git LFS 通过将仓库中的大文件替换为一个 指针文件 来达到这一目的。

  • 当你向仓库添加文件时,Git LFS 将文件内容替换为一个指针,并将实际文件存储在本地 Git LFS cache 中.

git add

  • 当你向服务器推送新的提交,任何被新提交引用的 Git LFS 文件会从本地 Git LFS cache 传送到与远程 Git 仓库关联的远程 Git LFS store 中.

git push

  • 当你从一个包含 Git LFS 指针的提交中检出文件时,指针会被替换回本地 Git LFS cache 中的文件,或者从远程 Git LFS store 中下载文件.

git checkout

Git LFS 的这些操作都是无缝集成的,不会影响你现有的 Git 工作流。你会发现 git clonegit pull 非常快,因为你不会下载每一个提交版本中的大文件,它只会下载你实际需要 checkout 的那个提交的大文件。

下载安装包

  • Linux 用户: 可以通过 PackageCloud 获取 Debian 和 RPM 安装包.

  • macOS 用户: 可以通过 Homebrew 安装,brew install git-lfs.

  • Windows 用户: Git LFS 已经包含在 Git for Windows,也可以通过 Chocolatey 包管理器安装最新版本.

  • 二进制安装包: Linux, macOS, Windows, and FreeBSD 还可以通过二进制安装包安装.

  • 从源代码编译: 可以使用最新版本的 Go 从源码编译 GitHub 仓库,从 Wiki 获取安装指南.

安装

使用二进制安装包安装

$ VERSION=v2.7.2
$ DISTRO=linux-amd64
$ tar xvzf git-lfs-${DISTRO}-${VERSION}.tar.gz
$ ./install.sh
Git LFS initialized.

安装脚本会执行以下操作:

  • Install Git LFS binaries onto the system $PATH

  • Run git lfs install to perform required global configuration changes.

$ git-lfs version
git-lfs/2.7.2 (GitHub; linux amd64; go 1.12.4; git 08a08ae0)

创建新的 Git LFS 仓库

$ mkdir lfs-repo
$ cd lfs-repo
$ git init
$ git lfs install
Updated pre-push hook.
Git LFS initialized.

这会在你的仓库安装一个 pre-push 钩子,在你执行 git push 时将 Git LFS 文件上传到服务器.

克隆 Git LFS 仓库

你可以像平常一样克隆一个 Git LFS 仓库,只有在检出默认分支 (通常是 master) 时才会自动下载需要的大文件。Git LFS 每检出一个指针文件,就会下载对应的大文件。

克隆或拉取时不下载 LFS 大文件

方法1:

GIT_LFS_SKIP_SMUDGE=1 git clone repository-url

方法2:

git lfs install --skip-smudge

--skip-smudge 会将如下选项添加到位于 ~/.gitconfig~/.config/git/config 的全局 git config 文件:

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

如果只想要某个仓库不下载 LFS 大文件,可以配置该仓库的本地 git config 选项

git lfs install --local --skip-smudge

恢复下载 LFS 大文件

恢复默认下载 LFS 大文件的话,只需要重新执行下面的命令

git lfs install

只下载某些文件或全部文件

下载单个文件

git lfs pull --include='filepath'

下载全部文件

git lfs pull

将下载的二进制文件恢复为 Pointer 文件

# 恢复所有文件
git reset --hard HEAD

# 恢复单个文件
git checkout HEAD -- filename

从二进制文件得到对应 Pointer 文件内容

git lfs pointer --file='filepath'

参考链接

加速仓库克隆

如果你克隆的仓库包含很多大文件,可以使用 git lfs clone 命令来提高性能。不同于一次下载一个文件,该命令会在 checkout 结束时,批量下载需要检出的大文件。这会大大减少 HTTP 请求和生成进程的数量 (在 Windows 系统尤其明显)。

拉取和检出

和克隆一样,使用 git pull 拉取 Git LFS 仓库,任何需要的 Git LFS 文件会在拉取完成并开始检出的过程中自动下载。

如果因为某些异常而检出失败,你可以使用 git lfs pull 下载缺失的 Git LFS 文件。

加速拉取

git lfs clone 一样,git lfs pull 会批量下载 Git LFS 文件。假如你知道远程仓库提交了大量新文件,你可能不希望在检出时自动一个个下载,而是用 git lfs pull 批量下载,可以在 git pull 时使用 -c 选项来覆盖原有配置:

git -c filter.lfs.smudge= -c filter.lfs.required=false pull && git lfs pull

简单起见,可以把它写成一个别名

git config --global alias.plfs "\!git -c filter.lfs.smudge= -c filter.lfs.required=false pull && git lfs pull"

git plfs

这会提高下载大量 Git LFS 文件时的性能 (同样,尤其在 Windows 上)

让 Git LFS 跟踪文件

当你向仓库添加一个新类型的大文件时,你需要使用 git lfs track 命令告诉 Git LFS 通过指定的模式来跟踪这个文件:

$ git lfs track "*.tgz"
Tracking *.tgz

执行完 git lfs track 你会发现,在执行命令的目录下产生了一个叫 .gitattributes 的文件,这是 Git 为匹配模式的文件绑定特殊行为的机制。Git LFS 会自动产生或更新这个文件,将指定模式的文件绑定到 Git LFS filter.

为了方便维护,应该只在仓库根目录执行 git lfs track 命令,将所有 Git LFS 模式都记录在根目录的 .gitattributes 文件里。

使用不带参数的 git lfs track 命令显示所有被 Git LFS 跟踪的模式

$ git lfs track
Listing tracked patterns
    *.tgz (.gitattributes)
Listing excluded patterns

可以从 .gitattributes 文件删除对应的模式来让 Git LFS 停止跟踪对应的文件,或者执行 git lfs untrack 命令:

$ git lfs untrack "*.tgz"
Untracking "*.tgz"

在不同主机间迁移 Git LFS 仓库

可以使用 git lfs fetch --allgit lfs push --all 将 Git LFS 仓库迁移到另一个主机,例如,

git clone --bare https://github.com/exampleuser/old-repository.git
cd old-repository.git
git lfs fetch --all
git push --mirror https://github.com/exampleuser/new-repository.git
git lfs push --all https://github.com/exampleuser/new-repository.git

获取额外的 Git LFS 文件

在不同主机间迁移 Git LFS 仓库中讲的,使用 git lfs fetch --all 可以获取你仓库中的所有 Git LFS 文件:

$ git lfs fetch --all
Scanning for all objects ever referenced...
✔ 23 objects found
Fetching objects...
Git LFS: (9 of 9 files, 14 skipped) 2.06 MB / 2.08 MB, 2.83 MB skipped

删除本地 Git LFS 文件

可以使用 git lfs prune 命令删除本地 Git LFS cache 中的大文件

$ git lfs prune
✔ 4 local objects, 33 retained
Pruning 4 files, (2.1 MB)
✔ Deleted 4 files

这会删除 旧的 Git LFS 文件,旧文件是不被下列提交对象引用的文件:

  • 当前检出的提交

  • 还没有被推送的提交

  • 最近的提交

可以使用 git lfs prune --dry-run 来测试多少文件会被删掉

$ git lfs prune --dry-run
✔ 4 local objects, 33 retained
4 files would be pruned (2.1 MB)

或者具体哪些文件会被删除

$ git lfs prune --dry-run --verbose
✔ 4 local objects, 33 retained
4 files would be pruned (2.1 MB)
* 4a3a36141cdcbe2a17f7bcf1a161d3394cf435ac386d1bff70bd4dad6cd96c48 (2.0 MB)
* 67ad640e562b99219111ed8941cb56a275ef8d43e67a3dac0027b4acd5de4a3e (6.3 KB)
* 6f506528dbf04a97e84d90cc45840f4a8100389f570b67ac206ba802c5cb798f (1.7 MB)
* a1d7f7cdd6dba7307b2bac2bcfa0973244688361a48d2cebe3f3bc30babcf1ab (615.7 KB)

使用 --verify-remote 选项在删除本地 Git LFS 文件前检查远程 Git LFS store 是否已经有一份拷贝

git lfs prune --verify-remote

这会让删除过程非常慢,但会保证你删除的文件可以从远程恢复。你可以使用 lfs.pruneverifyremotealways 选项让检查始终开启:

git config --global lfs.pruneverifyremotealways true

参考教程

Git LFS Tutorial