在 Git 中管理和存储大型文件

Azure DevOps Services | Azure DevOps Server 2022 - Azure DevOps Server 2019

Git 有助于保持较小的源代码占用空间,因为很容易辨别版本之间的差异,也很容易压缩代码。 在 Git 存储库中存储压缩效果不佳且不同版本之间完全不同的大型文件(例如二进制文件)时,会出现问题。 Git 之所以具有快速性能,是因为其能够从本地存储中处理和切换到文件的所有版本。

如果存储库中有无法区分的大型文件(例如二进制文件),每次提交对这些文件的更改时,存储库中都会保留这些文件的完整副本。 如果存储库中存在这些文件的多个版本,它们会大幅增加签出、分支、提取和克隆代码的时间。

应在 Git 中存储哪些类型的文件?

提交源代码,而非依赖项

团队使用编辑器和工具来创建和更新文件时,应将这些文件放入 Git 中,以便团队可以享受 Git 工作流的优势。 请勿将 DLL、库文件等其他类型的文件,以及并非由团队创建但代码依赖的其他依赖项提交到存储库中。 通过包管理将这些文件传递到系统。

部署包时,包管理会捆绑依赖项并在系统上安装文件。 对包进行版本控制,以确保只要这些环境具有相同的安装包,在一个环境中测试的代码就可在另一个环境中以相同的方式运行。

不要提交输出

请勿提交生成和测试中的二进制文件、日志、跟踪输出或诊断数据。 这些是代码的输出,而不是源代码本身。 通过工作项跟踪工具或通过团队文件共享与团队共享日志和跟踪信息。

在 Git 中存储不经常更新的小型二进制源

不经常更新的二进制源文件提交的版本相对较少。 如果文件大小较小,它们便不会占用太多空间。 Web 图像、图标和其他较小的艺术资产都属于这一类别。 最好将这些文件与源的其余部分一起存储在 Git 中,以便团队可使用一致的工作流。

重要

如果经常更新,即便是小型二进制文件也会引发问题。 例如,针对 100 KB 二进制文件的 100 个更改所用的存储量与 10 个更改对 1 MB 二进制文件的存储使用量一样多。 由于更新频率,较小的二进制文件比大型二进制文件会更频繁地降低分支性能。

不要提交经常更新的大型二进制资产

Git 会管理文件的一个主要版本,然后仅存储与该版本之间的差异,而此流程被称为增量存储。 通过增量存储和文件压缩,Git 可将整个代码历史记录存储在本地存储库中。 大型二进制文件通常会在不同版本之间完全更改,且通常已被压缩。 由于各版本之间的差异很大,因此 Git 难以管理这些文件。

Git 必须存储每个文件版本的全部内容,并且很难通过增量存储和压缩来节省空间。 存储这些文件的完整版本会导致存储库大小随着时间的推移而增大。 增大的存储库大小会降低分支性能、延长克隆时间并提高存储要求。

使用大型二进制源文件的策略

  • 不要提交压缩的数据存档。 最好取消压缩文件并提交可变源。 让 Git 处理存储库中的数据压缩操作。
  • 避免提交已编译的代码和其他二进制依赖项。 提交源并生成依赖项,或使用包管理解决方案对这些文件进行版本控制并将其提供给系统。
  • 以可区分的纯文本格式(例如 JSON)存储配置和其他结构化数据。

什么是 Git LFS?

源文件的各版本之间差异较大且更新频繁时,可使用 Git 大型文件存储 (LFS) 来管理这些文件类型。 Git LFS 是 Git 的扩展,它可提供用于描述提交到存储库中的大型文件的数据。 它会将二进制文件内容存储在单独的远程存储中。

当你在存储库中克隆和切换分支时,Git LFS 会从该远程存储下载正确的版本。 本地开发工具将以透明方式处理文件,就如同这些文件是直接提交到存储库一样。

好处

Git LFS 的优势在于,无论你的团队创建哪种文件,均可使用他们熟悉的端到端 Git 工作流。 LFS 可处理大型文件,以免它们对整个存储库产生不利影响。 此外,从版本 2.0 开始,Git LFS 将支持文件锁定,以帮助团队处理不可区分的大型资产,例如视频、声音和游戏地图。

Git LFS 在 Azure DevOps Services 中完全受支持且免费。 若要将 LFS 与 Visual Studio 结合使用,至少需要 Visual Studio 2015 Update 2 或更高版本。 只需按照安装客户端的说明操作,为本地存储库中的文件设置 LFS 跟踪,然后将更改推送到 Azure Repos。

限制

在采用 Git LFS 之前,需要考虑到它的某些缺点:

  • 团队使用的每个 Git 客户端均须安装 Git LFS 客户端并了解其跟踪配置
  • 如果未正确安装并配置 Git LFS 客户端,则在克隆存储库时无法看到通过 Git LFS 提交的二进制文件。 Git 会下载用于描述大型文件(由 Git LFS 提交到存储库)的数据,而不是实际的二进制文件。 在未安装 Git LFS 客户端的情况下提交大二进制文件会将二进制文件推送到存储库。
  • Git 无法合并两个不同二进制文件版本中的更改,即使这两个版本具有共同的父级。 如果两个人同时处理同一个文件,他们必须一起协调他们的更改,以避免覆盖对方的工作。 Git LFS 可提供文件锁定来帮助处理文件。 在开始工作之前,用户仍然必须注意拉取二进制文件资产的最新副本。
  • Azure Repos 目前不支持在包含 Git LFS 跟踪文件的存储库中使用安全外壳 (SSH)。
  • 如果用户通过 Web 界面将一个二进制文件拖拽到为 Git LFS 配置的存储库中,则会向存储库提交该二进制文件,而不是指针(后者需通过 Git LFS 客户端进行提交)。
  • 尽管没有严格的文件大小限制,但服务器的可用空间和当前工作负荷却可能会限制性能和功能。
  • 一个文件的上传时间限制为一个小时。

文件格式

对于 Git LFS 跟踪文件,写入到存储库的文件包含少数几行,且每行均有一个键/值对:

version https://git-lfs.github.com/spec/v1
oid a747cfbbef63fc0a3f5ffca332ae486ee7bf77c1d1b9b2de02e261ef97d085fe
size 4923023

注意

为版本值包含的 GitHub URL 仅会定义 LFS 指针文件类型。 它不是指向二进制文件的链接。

已知问题

如果将低于 2.4.0 的 LFS 版本与 Azure DevOps Server 配合使用,则需执行额外的设置步骤才能使用 NTLM 而不是 Kerberos 进行身份验证。 从 LFS 2.4.0 开始便不再需要执行此步骤,且强烈建议进行升级。