Elytra
BlogGit

Git 大文件、Git LFS 与 Perforce

Git 擅长管理文本与源代码,却在大二进制资产上容易失控。从 Git 为何「胖」、Git LFS 如何分流大文件,到 Perforce 为何长期占据游戏与影视管线,一文理清选型与落地要点。

很多人第一次把 .psd、.fbx、.wav 或几 GB 的贴图包 git add 进仓库时,Git 表面上一切正常:能 commit、能 push,同事也能 pull 下来。

直到某天:

  • git clone 从几分钟变成几小时;
  • 仓库体积从几十 MB 涨到几十 GB;
  • GitHub / GitLab 拒绝 push,提示单个文件超过 100 MB;
  • 某次改了一个 2 GB 的模型,历史里却多出了完整的 2 GB 副本。

这时才会意识到:Git 并不是为「大二进制资产 + 全历史可追溯」设计的。 它天生适合源代码;大文件需要额外策略,或者在另一类系统里管理。

若你还不熟悉 Git 的 Commit / Branch / 快照模型,建议先读 Git 核心概念与实用技巧。下文默认你已经知道:Git 用内容寻址的 blob 存文件,每个 Commit 指向一棵完整目录树。

核心结论

  • Git 的问题不是「不能存大文件」,而是「会把每个版本的大文件都留在历史里,且 clone 默认拉全历史」。
  • Git LFS 把大二进制移出 Git 对象库,仓库里只留指针;适合「仍以 Git 为主、偶尔有大资源」的团队。
  • Perforce(Helix Core) 从文件与变更集(changelist)出发,针对大二进制、独占检出、部分同步做了几十年优化;游戏、影视、大型 CAD 管线里仍很常见。
  • 没有银弹:小项目用 .gitignore + 外置资产即可;中型团队 Git + LFS 往往够用;资产量级、并发编辑模式、合规与权限要求,可能把你推向 Perforce 或混合方案。

1Git 为什么会在大文件上「失控」

1.1快照模型 + 内容寻址 = 历史越积越重

Git 的每个 Commit 逻辑上都是项目的一棵完整目录树。文件内容按 SHA 存成 blob;内容相同则 ID 相同,不同版本则是不同的 blob。

对文本源码,这很划算:改几行只多存一个小 blob,旧版本仍可追溯。对大二进制,规则一样残酷——你每次 commit 一个新版本的大文件,Git 就会在对象库里再存一份完整内容(packfile 里的 delta 对随机改动的二进制帮助有限)。

Commit A: model.fbx  → blob 2 GB
Commit B: 改材质后   → blob 2.1 GB   ← 历史里两份都在
Commit C: 再改一版   → blob 2.05 GB  ← 三份都在

git clone 默认会拉取所有分支的全部历史对象(除非你用浅克隆等技巧)。仓库里塞了 5 GB 的贴图,每个协作者第一次克隆往往就要吃下这 5 GB——而且随着历史增长只增不减。

1.2和「网盘」直觉的错位

很多人把 Git 当成「带版本的网盘」。实际上 Git 优化的是:

  • 分支、合并、追溯、diff、blame;
  • 分布式协作与离线提交;
  • 文本的高效存储与三方合并。

它没有为「只同步我当前需要的 200 MB 资产子集」或「二进制文件的语义合并」做一等公民支持。大二进制一旦进历史,即使用 git rm 删掉,对象仍在 .git 里,除非重写历史(git filter-repo、BFG 等)并强制所有人重新克隆。

1.3托管平台的硬限制

以 GitHub 为例:仓库大小建议不超过约 1 GB,单文件超过 50 MB 会警告、100 MB 会拒绝 push。GitLab、Bitbucket 等也有类似策略。这不是 Git 协议本身的限制,但会直接把「把资源当普通文件 commit」的路堵死。

已经 commit 了大文件?

不要指望 git rm + 新 commit 就能瘦身。需要从历史中彻底清除大文件对象,并协调全员换远程、重新 clone。常用工具:git-filter-repo、BFG Repo-Cleaner。操作前务必备份,并通知所有协作者。

2先问三个问题,再决定要不要「救」Git

在引入 LFS 或换 Perforce 之前,先厘清需求:

问题若答案是「是」倾向
大文件是否必须版本化、且要和代码同一条时间线追溯?需要与 commit 绑定Git LFS / Perforce / 混合
协作者是否只需要当前工作集的一小部分资产?是Perforce 部分同步、或外置 DAM + Git 存引用
二进制是否会被多人同时改同一文件?是Perforce 独占锁、或流程上拆分资产
仓库是否以文本源码为主,大文件只是点缀?是Git + LFS 通常足够
团队是否已深度绑定 GitHub Actions、PR 流程?是优先 LFS,慎换整套 VCS

若大文件本就不该进版本库(构建产物、Library/、缓存、本地导出),.gitignore + CI 产物存储才是正解,比任何 LFS 都省事。

3Git LFS:大文件「挂」在 Git 外面

Git LFS(Large File Storage)是 Git 的扩展:仓库里仍走正常的 commit / branch / merge,但大二进制实体存放在 LFS 服务端,Git 对象库里只存一个很小的指针文件(pointer)。

3.1指针长什么样

被 LFS 跟踪的文件,在 Git 树里类似:

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

git log、git blame、PR diff 看到的是指针;真正的 .fbx / .psd 在 git lfs pull 或 checkout 时由 LFS 客户端按 oid 拉取。

Loading diagram…

3.2基本用法

# 安装 Git LFS(各平台包管理器或官网安装包)
git lfs install

# 指定哪些路径走 LFS(写入 .gitattributes)
git lfs track "*.psd"
git lfs track "*.fbx"
git lfs track "Assets/Audio/**/*.wav"

git add .gitattributes
git add Assets/character.fbx
git commit -m "add character model via LFS"
git push

.gitattributes 必须提交进仓库,这样所有克隆者都会用同一套跟踪规则。

  • 工作流仍是 Git:branch、PR、tag、CI 触发方式不用推倒重来。
  • 克隆可只拉指针:GIT_LFS_SKIP_SMUDGE=1 git clone 可先拿代码结构,再按需 git lfs pull。
  • 托管集成:GitHub、GitLab、Bitbucket 等都提供 LFS 存储(通常有配额与带宽计费)。

3.3LFS 适合谁

  • 以源码仓库为主,附带模型、音频、设计稿、数据集;
  • 团队已习惯 GitHub/GitLab PR,不想整体迁移;
  • 大文件版本数可控,且能接受 LFS 存储成本。

游戏行业里 Unity / Unreal 项目若把 Library/、DerivedDataCache/、整包 Content/ 都塞进 Git,即使用 LFS 也往往得不偿失——更常见的做法是:代码与配置在 Git,资产在 Perforce、外置 NAS、或引擎自带的 Cloud / Accelerator。

4Perforce:为「大资产 + 大团队」长出来的模型

Perforce Helix Core(常简称 P4)在游戏与影视行业用了几十年。Epic、许多 AAA 工作室历史上都以 P4 + 自研或商业资产管线 为主;Git 更多是程序代码、工具脚本、基础设施即代码。

它和 Git 的设计重心不同,不是简单「另一个分布式快照库」。

4.1文件中心,而不是 Commit 图中心

Perforce 的核心是 depot(仓库)里的文件修订版本。你提交的是 changelist(变更集):一组文件的 add / edit / delete,带描述与关联任务单。历史是「文件 revision 序列」,而不是全局 DAG 式的 Commit 图。

对大二进制:

  • 可以配置 以二进制或文本方式存储,大文件有专门的类型与压缩策略;
  • 只 sync 你工作区需要的路径(p4 sync //depot/Project/Art/...),不必像默认 git clone 那样一次吃下整个历史的所有 blob;
  • 独占检出(exclusive checkout / +l 文件类型):对 .uasset、.psd 等声明为「独占」,一人编辑时他人无法提交冲突版本——用流程避免不可合并的二进制冲突。
Git 默认心智模型          Perforce 常见心智模型
─────────────────        ─────────────────────
clone 整库历史            sync 当前需要的目录
branch 廉价、常并存        常以 stream / branch 视图组织发布线
merge 三方合并文本        二进制靠锁或拆分资产
分布式、每人完整 .git      中央 server + 本地 workspace

4.2Stream:给发布线用的「有向图」

Perforce 的 Stream 类似「有规则的 branch 视图」:mainline、development、release、feature stream 有明确的父子与合并方向,适合「程序在 main、美术在 development stream、里程碑从 release 拉出」这类管线。和 Git Flow 有点像,但是围绕 文件路径与 stream 图 建模,而不是围绕 Commit DAG。

4.3与游戏引擎的共生

Unreal 官方长期提供 Perforce 集成;Unity 也有版本控制包可接 P4。引擎编辑器内的「检出 / 签入 / 冲突」与独占锁,是 Perforce 在资产工作流里仍占一席之地的现实原因——不是 P4 比 Git 「更先进」,而是问题域不同。

4.4Perforce 的代价

  • 中央服务器依赖强(虽有 replica、edge server,但心智仍是 server-centric);
  • 许可与运维:Helix Core 商业授权、HA、备份、与 Git 并行的双轨成本;
  • 开源与社区生态偏企业:个人开发者、小团队更熟悉 Git,招聘与培训有摩擦;
  • 与 Git 工具链割裂:GitHub PR、GitHub Actions 不能直接套在 P4 changelist 上,需要 Jenkins、Horde、自研 CI 或 Git + P4 混合。

混合管线很常见

不少工作室的实际形态是:程序与工具用 Git(或 GitHub),美术与关卡资产用 Perforce,构建机两边拉取,用版本清单或 build graph 对齐。选型不是「非黑即白」,而是边界画在哪里。

5三者对比:一张表不够,但总得有一张

维度纯 GitGit + LFSPerforce
文本源码极佳极佳良好
大二进制历史差(全量 blob 进历史)中(实体在 LFS,历史仍保留每版)优(按 revision、可部分 sync)
首次获取仓库常需全历史可跳过 smudge 按需拉 LFS可只 sync 子树
二进制并发编辑无原生锁无原生锁独占锁等原生支持
分布式 / 离线强强(指针在本地,实体需拉取)弱,依赖 server
与 GitHub 生态原生原生(LFS 配额另计)需桥接或分离
学习曲线(互联网从业者)低低~中中~高
典型场景库、Web、工具含资源的中型应用AAA 游戏、影视、大型 CAD

6落地建议:按规模选路径

个人 / 小原型:大资源不要进 Git。.gitignore 忽略 Library/、node_modules/、构建输出;大资产放网盘、对象存储或 releases,仓库里只留 README 说明版本与下载方式。

独立游戏 / 小团队(以代码为主):Git + LFS 跟踪 *.psd、*.wav、*.fbx 等;在 .gitattributes 里写清规则;CI 里加 git lfs pull;关注托管方 LFS 配额。

中型团队、资产量上来:评估是否要把整个 Assets/ 或 Content/ 迁出 Git。Unity 可考虑 Plastic SCM(现属 Unity)等更贴近引擎的资产方案;或引入 Perforce 只托管美术路径。

大型并发编辑、强合规、已有 P4 投资:以 Perforce(或商业混合方案)为主资产库,Git 管代码与 IaC;明确「哪条路径在哪个系统」,避免同一文件两套真相。

6.1无论选哪条路,都该遵守的纪律

  1. 构建产物、缓存、本地配置永不进库——这是免费的体积优化。
  2. 大文件进库前问一句:下一版会不会改?会不会有十个版本?若会,普通 Git 几乎必胖。
  3. LFS 的 .gitattributes 尽早提交,别等仓库已经 10 GB 再迁移。
  4. 独占资源在流程上就要拆文件——锁只是最后手段;可合并的粒度(材质、网格、动画分文件)比选 VCS 更重要。
  5. 与 Git 核心概念 里的原则一致:公共历史不要随意 force push;要清理 LFS/大文件历史时,当成「破坏性运维」,写好文档与备份。

7收束:问题域决定工具,而不是流行度

Git 赢得的是开源协作与源代码分发;Perforce 守住的是大二进制、独占编辑、部分同步的企业资产管线。Git LFS 是务实的折中:让你继续活在 Git 世界里,把最肥的那部分挪到旁边货架。

若你的痛点是「clone 太慢」,先查是不是误提交了生成物;若痛点是「美术和程序抢同一套 .uasset」,单靠 LFS 解决不了,需要锁或资产拆分;若整个项目就是「几万个二进制 revision」,就该认真评估 Perforce 或引擎原生资产系统,而不是在 Git 历史上堆更多 GB。

一句话

源代码用 Git 的思维;大资产用「谁存实体、谁管锁、谁只同步子集」的思维。 LFS 和 Perforce 都是这道边界上的不同答案,而不是谁取代谁。

CONTINUE EXPLORING

沿着这篇文章继续

在地图中查看

On this page