下载

Windows / Cygwin

https://git-lfs.github.com/

(Cygwin 下使用 Git LFS 请参考后文相关注意事项)

macOS

brew install git-lfs

Arch Linux

sudo pacman -S --noconfirm git-lfs

Debian >= 9 / Ubuntu >= 18.04

sudo apt-get update
sudo apt-get install git-lfs

其他系统或手动下载安装

https://github.com/github/git-lfs/releases

安装

Windows / Cygwin

根据安装向导安装下载的 .exe 包后,启动一个新的命令行窗口(以使包含 git-lfs 命令的目录能被加载进 PATH,并执行以下命令:

git lfs install

macOS / Arch Linux / Debian / Ubuntu

执行以下命令即可:

git lfs install

其他系统或手动下载安装

下载完压缩包后,打开命令行进入下载文件所处的目录,并执行以下命令:

tar xf git-lfs-*.tar.gz
cd git-lfs-*
sudo ./install.sh

配置

使用 git lfs track 追踪需要使用 Git LFS 管理的文件。如:

git lfs track "*.psd"

也可以手动编辑 Git 仓库根目录下的 .gitattributes 文件,如:

*.psd filter=lfs diff=lfs merge=lfs -text

常用 Git LFS 命令

# 查看当前使用 Git LFS 管理的匹配列表
git lfs track

# 使用 Git LFS 管理指定的文件
git lfs track "*.psd"

# 不再使用 Git LFS 管理指定的文件
git lfs untrack "*.psd"

# 类似 `git status`,查看当前 Git LFS 对象的状态
git lfs status

# 枚举目前所有被 Git LFS 管理的具体文件
git lfs ls-files

# 检查当前所用 Git LFS 的版本
git lfs version

# 针对使用了 LFS 的仓库进行了特别优化的 clone 命令,显著提升获取
# LFS 对象的速度,接受和 `git clone` 一样的参数。 [1] [2]
git lfs clone https://github.com/user/repo.git

[1] git lfs clone 通过合并获取 LFS 对象的请求,减少了 LFS API 的调用,并行化 LFS 对象的下载,从而达到显著的速度提升。git lfs clone 命令同样也兼容没有使用 LFS 的仓库。即无论要克隆的仓库是否使用 LFS,都可以使用 git lfs clone 命令来进行克隆。
[2] 目前最新版本的 git clone 已经能够提供与 git lfs clone 一致的性能,因此自 Git LFS 2.3.0 版本起,git lfs clone 已不再推荐使用。

迁移已有的 git 仓库使用 git lfs 管理

# 重写 master 分支,将历史提交中的 *.zip 都用 git lfs 进行管理
git lfs migrate import --include-ref=master --include="*.zip"

# 重写所有分支及标签,将历史提交中的 *.rar,*.zip 都用 git lfs 进行管理
git lfs migrate import --everything --include="*.rar,*.zip"

注意: 重写历史后的提交需执行 git commit --force,请确认在本地的操作合适无误后再进行提交。
注意: 如有迁移至 git lfs 前的仓库的多份拷贝,其他拷贝可能需要执行 git reset --hard origin/master 来重置其本地的分支,注意执行 git reset --hard 命令将会丢失本地的改动。

Git LFS 进阶使用

使用 Git LFS 的核心思想就是把需要进行版本管理、但又占用很大空间的那部分文件独立于 Git 仓库进行管理。从而加快克隆仓库本身的速度,同时获得灵活的管理 LFS 对象的能力。

默认情况下,只有当前 commit 下的 LFS 对象的当前版本才会被获取。

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

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

GIT_LFS_SKIP_SMUDGE=1 git clone https://github.com/user/repo.git
# 或
git -c filter.lfs.smudge= -c filter.lfs.required=false clone https://github.com/user/repo.git

注:GIT_LFS_SKIP_SMUDGE=1 及 git -c filter.lfs.smudge= -c filter.lfs.required=false 同样使用于其他 git 命令,如 checkoutreset 等。

只推送仓库本身,而不推送任何 LFS 对象

如果当前仓库启用了 LFS,而还需要将该仓库备份至其他不支持 LFS 的 git 服务器,那么可以在 push 时加上 --no-verify 参数来跳过 LFS 对象的提交:

git push --no-verify backup master

参考

获取当前 commit 下包含的 LFS 对象的当前版本

如果起初获取代码时,没有一并获取 LFS 对象,而随后又需要这些被 LFS 管理的文件时,可以单独执行 LFS 命令来获取并签出 LFS 对象:

git lfs fetch
git lfs checkout
# 或
git lfs pull

仅获取指定目录下的 LFS 对象

比如说,我们有一仓库,里面包含了许多源代码文件,以及一些图像、视频等资源文件,其目录结构如下:

zzz.buzz
├── images
│   ├── cat.png
│   ├── dog.gif
│   └── rabbit.webp
├── src
│   ├── buzz.css
│   ├── index.html
│   └── zzz.js
└── videos
    ├── chameleon.mp4
    └── iguana.webm

其中的 images/** 以及 videos/** 是被 LFS 所管理的。

但是,如果只想取 images 文件夹,而不想获取 videos 文件夹下的文件的话,我们就可以选择配置 LFS 下载对象时仅包含 images 文件夹:

git config lfs.fetchinclude 'images/**'

随后,git checkoutgit resetgit lfs fetchgit lfs pull 等命令就都会只处理所指定的文件夹。

类似地,我们也可以选择仅排除指定的文件夹:

git config lfs.fetchexclude 'videos/**'

也可以同时使用黑白名单规则,这样只有同时满足 include 规则和 exclude 规则的大文件才会被获取:

git config lfs.fetchinclude 'videos/**'
git config lfs.fetchexclude 'videos/chameleon.mp4'
# 在此例中,如此配置将只会获取 videos/iguana.webm 一个文件。

一次获取 LFS 对象的最近版本

Git LFS 相关命令在获取 LFS 对象时,默认仅会获取该对象当前被引用的版本,如果想要一次获取 LFS 对象的当前及最近版本的话,我们首先需要对最近进行定义:

git config lfs.fetchrecentcommitsdays 7

7 表示同时下载过去 7 天内的版本(相对于获取的 LFS 对象的时间),该项配置默认值为 0,即不获取过去的版本,而仅获取指定的版本。

有了对最近的定义后,我们可以选择在执行 git lfs fetch 命令时,加上 --recent 参数以同时获取最近版本;

或者配置

git config lfs.fetchrecentalways true

从而总是同时获取 LFS 对象的最近版本。

常见问题

  • 在安装 Git LFS 之前,克隆了使用 Git LFS 的仓库,则被 Git LFS 管理的文件会被显示为文本指针,而非具体的文件。

    查看这些文件指针,会发现类似如下内容:

    version https://git-lfs.github.com/spec/v1
    oid sha256:4b99dbe6fe6f646b2026de93481045bbf34f995559db15fce34d192f1f320ef4
    size 156154
    

    解决办法就是,手动执行获取 Git LFS 对象的命令:

    git lfs fetch
    git lfs checkout
    # 或
    git lfs pull
    
  • Git LFS 对象在本地仓库的存放位置?

    通过 Git LFS 所管理的对象实际在本地的存储位置是在 .git/lfs/objects 目录下,该目录根据对象的 sha256 值来组织。

    作为对比,Git 自身所管理的对象则是存储在 .git/objects 目录下,根据 commit, tree, blob, tag 的 sha1 值来组织。

  • 已经使用 git lfs track somefile 追踪了某个文件,但该文件并未以 LFS 存储。

    如果被 LFS 追踪管理的文件的大小为 0 的话,则该文件不会以 LFS 的形式存储起来。

    只有当一个文件至少有 1 个字节时,其才会以 LFS 的形式存储。

    注:一般使用 LFS 时,我们也不会用其追踪空文件,即使追踪了空文件,对于使用也没有任何影响。提到这点主要是为了消除在测试使用 LFS 时可能遇到的困惑。

  • 执行 git lfs fetch 或 git lfs pull 时报错

    batch request: exit status 255: Permission denied (publickey,gssapi-keyex,gssapi-with-mic).
    

    如果在克隆仓库时使用了 SSH 协议,而本地的 SSH 私钥又有密码保护,那么向服务器获取文件时就会报错,因为目前 Git LFS 不会向用户请求密码,从而导致认证失败。

    解决办法是使用 ssh-add 命令,预先加载好本地的 SSH 私钥,从而使得 Git LFS 能够访问到私钥。

  • 使用 Git LFS 时,报错缺失协议 (missing protocol),协议不支持 (unsupported protocol scheme) 或 batch request: unexpected end of JSON input。

    出现这种错误通常有两种原因:

    1. 其中第一种是克隆仓库时使用的地址没有包含用户名,如克隆时使用了类似 git clone github.com:user/repo.git 的命令,从而导致 Git LFS 错误地将服务器地址当作协议名来看待,而报出协议不支持的错误:

      [z@zzz.buzz lfs-test]$ git push
      Git LFS: (0 of 1 files) 0 B / 1 B
      Post git.zzz.buzz:z/lfs-test.git/info/lfs: unsupported protocol scheme "git.zzz.buzz"
      Post git.zzz.buzz:z/lfs-test.git/info/lfs: unsupported protocol scheme "git.zzz.buzz"
      error: failed to push some refs to 'git.zzz.buzz:z/lfs-test.git'
      

      解决办法就是在仓库地址中加上 git 用户名,如:

      git remote set-url origin git@github.com:user/repo.git
      
    2. 第二种原因则是克隆仓库时使用的是 SSH 协议,而使用的 Git 服务器不支持在 SSH 下使用 Git LFS(如低于 8.12 版本的 GitLab 及 Azure Repos),其解决办法为将克隆仓库时使用的 SSH 协议换为 HTTPS 协议即可。

      如原先 origin 设置为 git@github.com:user/repo.git,则可以运行如下命令:

      git remote set-url origin https://github.com/user/repo.git
      

      随后再执行 Git LFS 相关的命令。

      或使用 HTTPS 协议重新克隆仓库:

      git clone https://github.com/user/repo.git
      
  • git push 报错 i/o timeout

    如在 git push 时报类似如下错误:

    LFS: Put "https://.../info/lfs/objects/89ba03e8e99d29d0c5cd1703b6875fd1eca035e4cd3fd29f56f3e1eb2d8c3c25": read tcp 10.100.1.2:59508->13.107.42.20:443: i/o timeout
    

    可以尝试设置 git config http.version HTTP/1.1

    参考

Cygwin 环境下使用 Git LFS 的注意事项

在 Cygwin 下我们可以使用专门为 Cygwin 编译的 git,也可以使用为 Windows 编译的 git,两者在功能上本质是相同的。

但其实一般推荐在 Cygwin 下使用专门为 Cygwin 编译的 git,因为至少有以下好处:

  • 生成/处理的文件有正确的可执行位,core.filemode 也会和 linux 下一样被默认设为 true
  • core.symlinks 和 linux 下一样不会被设定为 false(默认为 true);
  • 且 core.ignorecase 和 Windows 下一样会被设置为 true

但是可惜的是,Git LFS 并没有专门为 Cygwin 编译的版本;而又幸运的是,为 Windows 编译的 Git LFS 也可以在 Cygwin 下使用,且在大部分情况下可以和专门为 Cygwin 编译的 Git 混合使用。

但是在执行 git lfs pull 或 git lfs checkout 命令时,混用为 Cygwin 编译的 Git 和为 Windows 编译的 Git LFS 就会由于两种环境对路径的不同解析而出错。

Error updating the git index:
error: ...: cannot add to the index - missing --add option?
fatal: Unable to process path ...

因而在执行以上两个命令时,必须同时使用为 Windows 编译的 Git 和 Git LFS,才不会出错。

  • 要想执行为 Cygwin 编译的 git,可以执行命令 git 或 /usr/bin/git
  • 而要执行为 Windows 编译的 git,则执行命令 "/cygdrive/c/Program Files/Git/cmd/git"

我们可以通过 git lfs env | head -2 来查看所用到的 Git 命令和 Git LFS 命令是来自 Windows 还是 Cygwin。

如使用 Cygwin 的 Git 和 Windows 的 Git LFS 会有以下结果:

git-lfs/1.5.4 (GitHub; windows amd64; go 1.7.4; git 71b637f1)
git version 2.5.1

而使用 Windows 的 Git 和 Windows 的 Git LFS 会有以下结果:

git-lfs/1.5.4 (GitHub; windows amd64; go 1.7.4; git 71b637f1)
git version 2.11.0.windows.1

注意看版本号中的 windows 字样来进行区分。

为了方便在 Cygwin 下同时使用为不同平台编译的 Git,我也写了一个脚本以自动在执行 Git LFS 相关命令时使用 Windows 的 Git,而在其他命令时使用 Cygwin 的 Git,该脚本也兼容其他 Linux 系统。

请参考 git.sh,具体改动见提交 git: Use Git built for Windows on Cygwin when working with LFS