· 技术文档

Git 和图片存储

核心问题:Git 与大文件的矛盾

如果你熟悉 Git,应该知道它最初是为管理文本文件而设计的,并不适合管理大型二进制文件(如图片、视频等)。这一局限性源于 Git 的基本工作机制:

  • 完整历史记录:Git 保存完整的提交历史,所有曾经加入并提交的文件都会存储在 .git 目录中,即使这些文件后来被删除了。克隆仓库时,默认会下载整个历史。

  • 快照而非差异:Git 不会仅存储文件的差异部分,而是在每次提交时存储文件的完整副本。这对文本文件来说很高效(因为差异计算简单且体积小),但对二进制文件却会导致仓库体积的快速增长。

“Git 存储的是内容快照,而非文件差异” —— Git 官方文档

随之而来的问题

随着项目历史的增长,频繁修改和提交的图片文件会导致:

  1. 仓库体积膨胀:即使只修改了图片的一小部分,Git 也会存储整个文件的新副本
  2. 克隆时间延长:新成员加入团队时,需要下载所有历史版本的图片
  3. 存储限制:许多 Git 托管平台对仓库大小有严格限制(通常为 5GB 左右)

传统解决方案

针对这些问题,开发者长期以来采用了多种策略:

1. 使用图床

优点

  • 仓库体积保持小巧
  • 图片可独立管理和优化

缺点

  • 公共图床存在隐私和持久性风险
  • 自建图床需要额外的基础设施
  • 工作流程变得复杂(先上传图片,再引用链接)

2. Git LFS(Large File Storage)

Git LFS 是一种 Git 扩展,专为处理大型文件设计:

# 安装 Git LFS
git lfs install

# 指定需要 LFS 管理的文件类型
git lfs track "*.png" "*.jpg"

# 克隆时跳过 LFS 文件(按需下载)
GIT_LFS_SKIP_SMUDGE=1 git clone <repository-url>

工作原理:将大文件存储在单独的服务器上,仓库中只保留文件指针。

优点

  • 减小 Git 仓库体积
  • 按需下载大文件
  • 与 Git 工作流无缝集成

缺点

  • 免费使用受限:GitHub 等平台对 LFS 有严格的免费下载量限制(如 GitHub 每月仅 1GB),超出需付费
  • 增加复杂性:即使是简单的 pull/push 操作也涉及额外的 LFS 对象传输
  • 协作障碍:当团队成员不熟悉 LFS 工作流时可能出现混淆
  • 历史文件问题:对已提交到仓库的大文件转为 LFS 管理比较麻烦

3. Git Submodule

将项目拆分为多个子仓库,图片文件单独存放:

# 添加图片子模块
git submodule add <repository-url> images

# 克隆主项目及其子模块
git clone --recurse-submodules <main-repository-url>

优点

  • 可以按需克隆子模块
  • 图片集合可以独立管理和版本控制

缺点

  • 增加项目复杂性
  • 子模块操作对新手不友好
  • 维护多个仓库的额外工作

4. 清理历史记录

使用 git-filter-repo 等工具可以彻底清除某些大文件的历史记录:

# 安装 git-filter-repo
pip install git-filter-repo

# 移除所有大于 10MB 的文件的历史记录
git filter-repo --strip-blobs-bigger-than 10M

注意:这会重写历史,所有团队成员需要重新克隆仓库。

现代 Git 解决方案

近年来,Git 引入了两个强大功能来直接解决大文件问题:

1. 部分克隆 (Partial Clone)

允许只获取仓库的部分内容:

# 不下载任何文件内容,只获取目录结构
git clone --filter=blob:none <repository-url>

# 仅排除大文件
git clone --filter=blob:limit=1m <repository-url>

2. 稀疏检出 (Sparse Checkout)

与部分克隆配合使用,只检出项目的部分内容:

# 完整流程示例
git clone --filter=blob:none --no-checkout <repository-url>
cd <repository-directory>
git sparse-checkout init --cone
git sparse-checkout set <dir1> <dir2> ...
git checkout

这种组合使用的方式类似于下载种子文件时选择性下载某些内容,既节省了网络流量,又减少了本地存储需求。

支持情况

目前各平台对这些新功能的支持情况:

平台部分克隆稀疏检出
GitHub
GitLab
Bitbucket
腾讯 Coding
阿里云云效

实用建议

针对这个项目,我们建议:

  1. 早期阶段:直接在 Git 中管理图片文件是可行的,特别是当图片数量有限且不经常修改时。

  2. 中期优化:当观察到克隆时间变长,可考虑使用部分克隆和稀疏检出功能。

  3. 图片最佳实践

    • 在提交前优化图片(压缩、调整尺寸)
    • 避免频繁修改同一图片
    • 考虑使用矢量图形(如 SVG)替代位图
  4. 监控仓库大小:定期检查 .git 目录大小,发现异常增长时及时处理。

为什么这些功能来得这么晚?

你可能会问,为什么像”按需下载”这样的基本功能,Git 直到最近几年才支持?这是因为 Git 的设计哲学与普通下载工具不同:

  • Git 需要确保所有操作(包括分支、合并、历史查看)在部分数据情况下仍能正确工作
  • 需要解决如何跟踪和操作那些尚未下载的文件
  • 所有 Git 命令和客户端都需要适配这种新模式
  • 服务端也需要支持新的协议扩展

扩展阅读

若想深入了解这些概念,推荐以下资源:

GitLab 官方博客

GitHub 官方博客

    返回