Git 201 – Git LFS in Action

Git Large File Storage (LFS) 是一个由 GitHub 开发的 Git 扩展项目, 用于增强 Git 对大文件追踪的支持.

Git 对于一些二进制,及大文件的处理一直不是很好,二进制文件的对比,差量存储都未能优化处理。

这导致了在 Git 的仓库中,会储存二进制文件的所有历史全量版本。这样一来,会给储存带来很大的困扰,同时在进行 Clone/Fetch/Pull 时都会产生大量的流量,特别是在一些网络不大好的地方。

而 Git LFS 就是为了解决这一问题而产生的工具。它将标记的大文件保存至另外的地方,而在主仓库仅保留其轻量级指针。这样在获取版本时,只更新主库对应的大文件,会更加轻量,高效。

主要的特征如下:

  • 超大文件支持
  • 减小仓库占用空间
  • 更高的远程获取效率
  • 和 Git 操作保持一致

▎安装

1
2
# macOS with HomeBrew
$ brew install git-lfs

为 Git 账号进行初始化设置(只需要一次)

1
$ git lfs install

▎使用

> 配置 LFS

在需要使用 LFS 的项目中,通过 git lfs trace 配置需要加入 LFS 的文件,可以使用通配符。

编辑完之后,会创建或修改项目的 .gitattributes,也可以通过手工修改该文件来进行配置。

1
$ git lfs track "*.bin"

> 将 .gitattributes 加入到仓库中

1
2
3
$ git add .gitattributes

$ git commit -m "track files using Git LFS"

执行 push 时会输出一些 LFS 的指示

1
2
3
4
5
6
7
$ git push

Uploading LFS objects: 100% (1/1), 104 KB | 0 B/s, done.

Enumerating objects: 6, done.
Total 4 (delta 2), reused 1 (delta 0)
remote: Resolving deltas: 100% (2/2), completed with 2 local objects.

> 查看文件的状态

1
$ git show HEAD:cat.bin

输出当前文件关于 LFS 的一些值:

1
2
3
4
5
6
7
On branch master
Your branch is up to date with 'origin/master'.

Changes not staged for commit:
version https://git-lfs.github.com/spec/v1
oid sha256:ce5c0e9713c54368d4a6b9a30c46503bac70c293091f0e60e30aa14f6813cca0
size 103890

▎操作命令

> 基本操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ git lfs track
# 将一个或者一类文件以 git lfs 的方式加入到版本控制中 (实质是修改 .gitattributes 文件)

$ git lfs untrack
# 将一个或者一类文件从仓库中移除

$ git lfs status
# 类似于 git status , 显示 git lfs 方式的文件在 暂存区的状态

$ git lfs lock
# 锁定一个或者一些文件, 只允许当前的用户对这些文件进行修改, 防止在多人协作的场景下冲突

$ git lfs unlock
# 同上, 解锁一个或者一些文件

> 其它操作

显示当前仓库的 env 配置:

1
$ git lfs env

LFS 文件操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ git lfs ls-files
# 展示使用 git lfs 方式的文件列表

$ git lfs prune
# 删除全部旧的 Git LFS 文件

$ git lfs fetch
$ git lfs pull
# 不通过 git 获取文件

$ git lfs push
# 不通过 git 单独提交文件

$ git lfs checkout
# 取消文件修改,切换版本

正常情况下会随着 git pull/push 一起执行
如果在 git pull/push 的过程中断了, 导致二进制文件没有被拉取的时候, 可以使用这些命令(支持断点续传,速度不慢)

▎常见问题

> Git LFS 迁移 - 已提交 git 文件的处理

已经被 git 提交了,怎么转到 LFS 里呢?

这时就要把 git 的旧记录也一并删除,不然该文件及历史版本,会一直存在 Git 的数据中,每次获取都占用大量带宽和存储空间。

1
2
3
4
5
6
7
8
$ git lfs migrate

# 用来将当前已经被 git 储存库保存的文件以 git lfs 的保存 (将 git 对象转为 lfs 对象)
# 例如如果将当前远程不存在的的所有 mp4 文件清除
$ git lfs migrate import --include="*.mp4"

# 如果是已经上传到其它服务器的内容, 则需要指定分支 (可能需要 push --force)
$ git lfs migrate import --include="*.mp4" --include-ref=refs/origin/master --include-ref=refs/origin/dev --include-ref=refs/origin/test

上述操作,会将所有的 git 对象,转换为 git-lfs 对象,git 历史记录会受影响。

但 .git 目录下依然储存着该文件,需要进行清空处理:

1
2
# 然后使用如下命令清理 .git 目录
$ git reflog expire --expire-unreachable=now --all && git gc --prune=now

> 如何只获取仓库本身,而不获取任何 LFS 对象?

如果相关工作不涉及到被 Git LFS 所管理的文件的话,可以选择只获取 Git 仓库自身的内容,而完全跳过 LFS 对象的获取。

1
$ GIT_LFS_SKIP_SMUDGE=1 git clone git@github.com:username/my_lfs_repo.git destination_dir

使用该方式 Clone 仓库后,又需要这些 LFS 管理的文件了,如何再次拉取下文件呢?

我们可以通过如下 Git LFS 的 pull 命令进行拉取:

1
$ git lfs pull

> 如何获取历史版本

Git LFS 文件,每次提交时,都会将文件的索引记录在对应的 Git 的 commit 中。

所以只要通过 git checkout 就能恢复出对应的版本。

> 更复杂的场景

如果一个项目中,有很多用 LFS 管理的文件,有一些目录(或文件)不是通用的,但是另一些是必要的。那么如何设置在 clone 或 checkout 时只下载必要(默认)的 LFS 上的文件呢?需要时,再去下载另一部分文件。

可以用在 git config 中设置(要先保证该路径已被 git lfs track 追踪):

1
$ git config -f .lfsconfig lfs.fetchexclude="*.mp4"

这个命令会生成一个.lfsconfig文件(如果不存在),并在其中加入。

1
2
[lfs]
fetchexclude = *.mp4

这样配置后,每次 checkout 和 clone 就会过滤掉当前目录下以 .mp4 的为结尾的文件。

那如果需要这些文件时,要怎么办呢?

可以通过上文中的 git lfs pull 进行复写,如:

1
git lfs pull -X ''

就会在拉取时,去除 fetchexclude 选项。

▎GitHub 中关于 LFS 的使用

GitHub 在 Billing 账单页面可以账号的 LFS Data 使用情况。

默认每个账号都有 1GB 的免费存储空间和 1GB 的免费带宽,可以通过购买,来添加更多的容量及带宽。

▎REF::