
很多团队在项目初期根本不把资源文件当回事等做到一半才发现设计稿乱放、模型文件版本对不上、图标素材找不到最终版、打包体积莫名膨胀整个项目进度被文件管理硬生生拖慢。我经历过太多次这种状况所以后来每接手一个从 0 到 1 的项目第一件事不是写代码而是先把资源文件的管控流程立起来。这篇文章就是我自己在多个项目里反复打磨出来的那套完整流程从怎么给资源文件分类、怎么定目录规范到怎么选 Git LFS、怎么配合 CI 做自动校验再到怎么让团队成员愿意遵守每一步都有可落地的方案和实际踩坑记录。无论你是技术负责人、项目经理还是刚要搭建项目规范的研发工程师这套流程都能直接拿过去用帮你绕开我在坑里摔过的那些跟头。1. 动工前先盘家底资源文件分类与边界划分1.1 别急着建目录先把资源的定义搞清楚我见过不少团队一上来就建了个名为资源的文件夹然后什么都往里扔最后这个文件夹成了新的垃圾堆。所以在设计管控流程之前必须先回答一个问题哪些文件算资源文件我的经验是把资源文件分成三大类每一类的管控策略完全不同。第一类是设计源文件包括 PSD、AI、Figma 的导出文件、Sketch 文件以及 3D 建模用的 Blender、C4D、Maya 源文件。这类文件的特征是体积大、二进制格式、无法用文本 diff 工具比较内容差异而且通常只有设计师和少数开发会用。第二类是静态素材包括图片PNG、JPG、WebP、SVG、GIF、字体TTF、OTF、WOFF2、音视频MP3、MP4、WAV、图标文件等。这类文件是最终打包进产品里的直接面向用户必须有明确的版本管理和引用规范。第三类是配置与数据文件比如 JSON 配置、iOS 的 Info.plist、Android 的 gradle 配置、游戏项目的关卡数据表、策划配置的 CSV 等。这类文件虽然很多是文本格式但往往承载着业务逻辑一旦配错就会引发线上事故。值得注意的是代码文件比如 .java、.ts、.py、.go不在资源文件的管控范围内它们走代码 review 和分支管理流程。还有本地临时文件、构建产物比如 node_modules、build 目录、DerivedData、IDE 配置文件这些都不应该进入资源管控仓库而是通过 .gitignore 排除掉。做这个分类的动作本质上是在划定责任边界。设计源文件属于创意资产静态素材属于交付资产配置数据属于业务资产三者产生的时间点、变更频率、影响范围完全不同混在一起管理只会让规则变得极其复杂最后谁都执行不下去。1.2 盘点现状与摸底团队协作方式分类标准定好之后下一步要做的是摸清团队现状。我通常会用一份简单的表格让每个角色自己填写他们日常接触的资源文件类型。这个动作看起来简单实际作用很大。有一次我在一家做移动端 App 的公司梳理流程后端工程师说他们只需要 API 文档前端工程师说需要设计标注和切图设计师说需要统一的设计稿管理工具测试工程师说需要完整的安装包和配置文件。看似需求不冲突但仔细一问才发现他们各自手里的同一份设计稿竟然有四个不同的版本源头就在于没有约定统一的存储位置和命名规则。摸底的时候还要关注几个关键信息团队规模多大是集中办公还是远程协作项目周期多长是短期版本迭代还是长期维护现有工具有哪些是用了 Git 还是 SVN有没有企业内部的文件服务器或对象存储协同角色有哪些设计师是团队内部还是外包团队运营有没有素材上传需求。这些信息决定了后面流程设计的复杂度。一个 5 人小团队和 50 人跨部门团队管控的粒度完全不同照搬大厂方案只会让流程变成负担。我见过最典型的问题是把资源文件直接传到 Git 仓库然后仓库体积在几个月内涨到几个 GB每次 git clone 都耗时十几分钟团队成员苦不堪言。实际上 Git 本身就不是为管理二进制大文件设计的因为每次提交都会保留完整的历史版本一个 50MB 的 PSD 文件改上十几次仓库里就会存储 500MB 以上的历史数据而且这些数据永远无法被简单清理。所以流程设计的核心原则应该是代码走 Git大体积二进制走专门的存储小体积静态素材走 Git LFS各归其位互不干扰。2. 定好规矩再动工目录规范与命名体系设计2.1 一套能看懂且能自动检查的目录结构目录结构的设计决定了资源文件能不能被快速找到、能不能被自动化工具处理。我推荐的目录结构遵循一个核心思想按层级 类型 用途来组织而不是按提交人或时间来组织。一个经过验证的通用结构是这样的assets/ ├── 00_global/ # 全局共享资源禁止放业务相关文件 │ ├── styles/ # 全局样式文件如 CSS、Design Tokens │ ├── fonts/ # 字体文件标注授权信息 │ ├── icons/ # 通用图标按尺寸细分目录 │ └── images/ # 通用插图、背景图、默认图 ├── 10_module_a/ # 业务模块 A按业务功能命名 │ ├── images/ │ ├── data/ # 配置文件、JSON、CSV │ └── docs/ # 模块相关的设计说明、接口说明 ├── 20_module_b/ # 业务模块 B │ ├── images/ │ ├── data/ │ └── docs/ ├── 30_shared_3rd/ # 第三方资源如外部 SDK 资源、开源库附带资源 └── 40_release/ # 发布产物、打包资源、渠道素材为什么模块目录前要加数字前缀因为数字前缀能让文件按业务优先级排序而且在自动化脚本里可以直接用正则提取模块名。另外_ 下划线比 - 连字符更不容易在文件名里引发歧义这是我实际踩过坑后做的调整——之前用 hybird-app 作为目录名结果脚本解析时把连字符当成了减号引发了一个很隐蔽的 bug。模块划分的粒度要与业务模块保持一致而不是与技术类型保持一致。比如一个商城项目模块应该是首页、商品、购物车、订单、个人中心而不是图片、数据、文档因为资源文件的使用场景是跟着业务走的按业务分目录能直接映射到代码结构找起来快也能避免文件重复存放。2.2 命名规则让人能看懂最好 40ms 内找到任何文件文件命名是管控流程里最容易被忽略、却最能体现执行力的细节。我在流程里强制规定文件命名必须遵循以下公式[业务模块]_[文件类型]_[具体描述]_[版本号].[扩展名]举一些实际例子首页轮播图就是home_banner_main_v1.2.png个人中心默认头像user_avatar_default_v2.1.png商品详情页样式配置product_detail_style_config_v1.0.json。如果文件还没有定稿用_draft后缀标注定稿后再更新版本号并移除 draft 标记。版本号统一用vX.Y格式X 是大版本Y 是小版本。这里有个容易被忽略的细节文件内部的版本号必须与 Git 提交信息中的版本描述保持一致。我见过设计师改了一版图标命名里写的是 v2.0实际上传的那张图内容和 v1.0 完全一样等于白提交。文件名里永远不要出现最终版绝对不改了新旧副本最终版2这类词。这些话术会直接导致文件失控。我之前统计过一个团队里文件名的最高纪录一个 300MB 的 PSD 文件叫KV_绝对最终版_真的不改了_v5_final_副本.psd文件名快 50 个字符内容根本不知道是谁做的。命名规则的检查可以写成自动化脚本接入 Git 的 pre-commit 钩子检查不合规的文件名直接拦截、拒绝提交这样不用靠自觉也能保证规则落地。2.3 大文件与小文件的存储策略引入 Git LFS目录和命名确定后最核心的技术决策来了资源文件用什么工具管理。我的标准很直接单个文件超过 50MB 的设计源文件、音视频源文件、3D 模型文件不要进 Git 仓库使用独立的对象存储或文件服务器统一管理。单个文件在 50MB 以内的图片、图标、字体、配置文件必须进入 Git 仓库并用 Git LFS 管理。之所以以 50MB 为界是因为 Git LFS 在 50MB 以下性能尚可超过这个阈值就会明显拖慢拉取和提交速度而大型文件的存储用对象存储更合适。实际的 Git LFS 安装配置非常简单macOS 上brew install git-lfsUbuntu 上用apt install git-lfs然后git lfs install全局启用。接着在仓库里定义哪些后缀走 LFSgit lfs track *.psd git lfs track *.ai git lfs track *.sketch git lfs track *.png git lfs track *.jpg git lfs track *.ttf git lfs track *.otf执行完后.gitattributes文件会被创建这个文件要提交到仓库里团队其他成员才能同步规则。原理上Git LFS 是把大文件内容替换成一个指针文件推送到远端实际二进制内容存到 LFS 的独立存储区这样 Git 仓库本身很小克隆速度飞快。使用 Git LFS 有一个必须注意的铁律一旦确认文件走 LFS 管理就不要把同一文件既放到 LFS 又直接提交到 Git 仓库否则会在仓库里留下一个巨大的历史对象根本清不掉。LFS 本身建议在项目初始化时就启用如果等项目跑了大半年再迁移历史提交里的二进制文件已经沉淀在 Git 历史中迁移会非常痛苦。3. 让流程自动运转权限体系与协作规范落地3.1 谁能改、谁能看、谁能合入说实话绝大多数团队在资源文件管控上失效不是因为规矩不够好而是因为没有定义谁能做什么这件事。尤其是资源文件最容易出现的情况是开发人员直接改了设计图里的某个图层然后提了 MR设计师根本不知道或者产品经理绕过流程直接把素材传到了远端等发布时才发现资源和设计稿不一致。我在流程里明确划分了四类角色资源所有者Owner通常是设计师、内容运营或数据配置负责人负责创建、更新和删除资源文件对资源内容质量负责。资源审核者Reviewer通常是技术负责人或资深工程师负责确认资源文件的命名是否合规、目录是否正确、是否会引发体积膨胀等问题。资产维护者Maintainer负责仓库权限管理、Git LFS 配额、分支保护规则和 CI 脚本维护通常是 DevOps 或后端核心开发者。普通消费方Reader开发工程师、测试工程师只有读取权限不能直接修改资源文件。在 Git 平台上的操作权限我给的是这套建议Master 分支只有 Maintainer 能合并资源文件必须经过 MR/PR 流程禁止直接 push 到 Master资源文件目录单独设置 CODEOWNERS文件发生变更时自动通知对应负责人大文件的删除和重命名必须走 MR让历史变更可追溯。我记得第一次把这套权限搬到 GitLab 上时设计师很不适应说我传个图还要走审批太慢了吧。后来我加了一步对于图片、音视频这类不涉及业务逻辑的纯资源允许直接 push 到一个名为draft-assets的分支只有最终定稿才走 MR 合入 Master。这个折中方案既保证了流程的正式性又不扼杀设计师的即时创作流。3.2 分支策略资源文件与代码同步走还是分开走这里必须聊一个很多人纠结的点资源文件到底和代码一起做分支还是单独维护一个资源分支实际情况分两种。如果项目是传统的前后端分离架构资源文件和代码同时发布我强烈建议资源文件和代码走同一个 MR 流程即同一分支中同时提交代码变更和资源变更因为在代码里引用图片、JSON 配置时资源必须与代码处于同一版本基准否则会有资源对不上代码的经典问题。但如果项目是跨端项目同一套 UI 资源同时用于 Web、Android、iOS或者游戏项目美术资源量大、独立迭代那就单独建一个assets仓库或者用同一仓库里的assets目录配合独立的分支管理通过子模块或者专门的定义清单来让代码引用指定版本资源。这里又有一个实际坑。我在一个 Flutter 项目里让团队共用一套资源仓库代码仓库通过submodule引用。结果某个成员更新了资源仓库的最新提交但代码仓库里的 submodule 指针没有同步导致打包时用了错误的图片版本线上出现一个大大的破图。后来我们把 submodule 换成了 Git LFS 固定 tag 策略代码发布时锁定资源 tag彻底堵住了这个洞。所以我的心法是能用同一仓库解决的尽量不搞多个仓库必须拆开的时候一定要有显示引用版本的机制绝不能依赖默认最新。3.3 发布与回滚资源文件的版本标记策略资源文件的版本标记核心目标只有一个让任何时刻的构建产物都能对应到唯一的资源集合。我把资源版本标记做了三层。第一层是 Git 提交的 commit SHA天然唯一且不可变。第二层是 Git Tag格式为assets-vX.Y.Z每次资源定稿发布时打一个 tag便于 README 或配置文件中引用。第三层是文件内的版本说明比如 JSON 配置里写resource_version: 1.4.2方便运行时排查环境资源版本。实际发布流程我在 CI 管道里做了固化开发或者设计师将资源合入 Master 后CI 会构建资源产物包上传到对象存储或 CDN如果资源需要回滚管理员只需将资源 tag 回退到上一个 T-1 版本重新触发 CI产物会立刻替换到对应位置。整套流程的核心原则是资源产物一旦发布就不可变回滚靠切换版本而不是覆盖内容。这样做之后线上问题排查询问这个版本的资源是谁在什么时间改的只要看一眼 tag 历史即可解决不用再翻聊天记录。4. 从 0 到 1 落地的实操全记录4.1 初始化仓库一天的实操流程记录我挑一个曾经完整落地过的移动端项目把从 0 到 1 的实操步骤完整还原一遍。第一步创建统一的资源仓库。我先在 Git 服务上新建app-assets仓库并在仓库根目录创建上面描述过的 assets 目录结构然后初始化 Git添加初始 README。一开始我就在根目录准备好了README.md内容包括资源目录说明、命名规范、Git LFS 配置、常见命令、负责人列表和版本标记策略。这步几十分钟就能完成但它是一切的基础。第二步配置 Git LFS。连续执行git lfs install和git lfs track命令把设计源文件、图片、字体等后缀加进规则然后提交.gitattributes。这里有一个关键动作一定要在仓库有大量文件之前启用 LFS否则后面排查仓库体积时会后悔。第三步导入现有资源。这一步最费力要把团队手里散落的资源文件收集、重命名、按目录归位。我按大类先让各个角色的负责人把文件放到一个临时中转目录我再统一按规范整理归位。整理的同时做一次去重同一个文件的不同版本只保留指定的版本废弃的旧版本通过 Git 历史保留而不是占用工作区空间。导入完成后提交一个初始版本并打上assets-v0.9.0标记。第四步配置分支保护与权限。在 Git 服务后台设置 Master 分支保护禁止直接 push合并资源文件必须走 MR并设置 CODEOWNERS 为对应角色的负责人。第五步写自动化检查脚本。在scripts/check_assets.sh里做了两件事检查新增文件的命名是否符合规则检查是否有超过 50MB 的文件未通过 LFS 管理而直接提交到仓库。通过 pre-commit 钩子和 CI 两步卡点来强制规则落地。第六步团队培训与试运行。给团队讲清楚资源流向、常用命令和出现问题怎么办试运行期间放宽检查规则允许犯错但不允许不改两周后收紧到严格模式。4.2 自动化检查脚本两条最强卡点策略先看代码里最核心的卡点脚本。第一段是检查文件名规范的#!/bin/bash # scripts/check_assets.sh # 检查不合规的资源文件命名 PATTERN^[a-z0-9_]_[a-z0-9_]_[a-z0-9_]_v[0-9]\.[0-9]\.[a-z0-9]$ ERROR_COUNT0 for file in $(git diff --cached --name-only --diff-filterACM -- assets/*); do basename$(basename $file) # 允许目录本身跳过目录检查 if [[ -d $file ]]; then continue fi # 允许 README/LICENCE 等说明性文件 case $basename in README*|LICENSE*|NOTICE*|CHANGELOG*) continue ;; esac if [[ ! $basename ~ $PATTERN ]]; then echo 不合规文件名: $file ERROR_COUNT$((ERROR_COUNT 1)) fi done if [ $ERROR_COUNT -gt 0 ]; then echo 发现 $ERROR_COUNT 个不合规文件请按规范修改后重新提交。 exit 1 fi echo 资源文件命名检查通过。这里使用的正则要求文件名是[模块]_[类型]_[描述]_v[主版本].[次版本].[扩展名]格式允许小写字母、数字、下划线禁止空格和特殊符号。实际执行时如果设计文件中资源名是大写的会被直接拦截。一开始设计师抱怨为什么不能有驼峰命名后来我们约定所有文件名推荐小写下划线解决了跨平台大小写敏感问题。第二段脚本检查是否有大文件绕过 LFS之所以放在 pre-commit而不是等推送后再发现是为了把修正成本降到最低#!/bin/bash # scripts/pre-commit # 检查为走 LFS 的大文件 MAX_SIZE_MB50 MAX_SIZE_BYTES$((MAX_SIZE_MB * 1024 * 1024)) ERROR_COUNT0 # 获取 staged 文件列表排除 LFS 文件 for file in $(git diff --cached --name-only --diff-filterACM); do # 排除目录 if [ -d $file ]; then continue fi size$(stat -f%z $file 2/dev/null || stat -c%s $file 2/dev/null) if [ $size -gt $MAX_SIZE_BYTES ]; then # 检查该路径是否在 .gitattributes 中标记为 LFS 管理 if ! git check-attr filter $file | grep -q lfs; then echo 大文件未通过 LFS 管理: $file ($size 字节) ERROR_COUNT$((ERROR_COUNT 1)) fi fi done if [ $ERROR_COUNT -gt 0 ]; then echo 有 $ERROR_COUNT 个大文件未配置 LFS请先执行: git lfs track 相应后缀 exit 1 fi这段脚本在 macOS 和 Linux 上略有不同原因是 macOS 的stat参数是-fLinux 是-c我在写脚本时做了兼容处理。pre-commit 钩子可以通过pre-commit框架统一管理也可以直接放到.git/hooks/pre-commit下但团队里用框架更可以统一版本。实际跑下来这个脚本拦截过的最典型问题是一个同事把一个 300MB 的录屏文件直接git add进去如果没有拦截这个仓库就完蛋了。4.3 仓库体积排查一个必须掌握的救火技能即便有这些防线还是可能遇到仓库体积失控的问题。这时候常用git rev-list --objects --all来查看所有文件的历史统计用git cat-file --batch-check来查具体对象大小。找到大对象后很多人第一反应是直接删除但这里有个误区仅删除当前版本的文件并不能清理 Git 历史历史提交里的对象依然存在。真正要清理的话要么用git filter-repo改写历史要么干脆重建仓库并保留最新版本后者对一般团队来说往往更快更安全。有一回我们团队误提交了一个 800MB 的数据库备份文件发现时仓库已经推到了远端而且大家都在拉这个仓库。我评估了一下方案直接用 filter-repo 改写了历史然后git push --force强制推送到远端。当然这么做有风险团队其他成员必须重新克隆仓库因为在 force push 之后旧的引用会被破坏。所以我建议不要轻易对远端做历史改写正确做法是在审计脚本里加入git rev-list --objects --all | grep -E ^.*\.(zip|sql|psd)$这类定期扫描尽早发现异常对象。4.4 如何让团队真正遵守这套流程最后说说落地时最核心的问题规范再好团队不执行等于零。我的经验可以分为三条硬规则和一条软技巧。硬规则一卡点前置。不允许先提交后补规范提交前第一步就是自动检查不通过根本进不了仓库。硬规则二可视化透明。仓库根目录放一块状态看板用 CI 展示资源目录体积变化、命名规范抽查、远程仓库体积趋势让每个人直观看到资源管控的实时状态。硬规则三异常自动告警。CI 检测到超过阈值的文件提交时立刻在 IM 工具群里推送告警并阻断合并流程。这样不用点名批评系统自己就把问题暴露了。软技巧是给设计师和产品人员留出一条快速通道。即提供一个 FIFS 或者一个对象存储的上传链接他们可以直接把超大文件传到临时区并在 MR 描述中注明用途由维护者后期统一迁移到正式目录并做 LFS 管理。这套流程的本质不是强迫单角色改变工作习惯而是让资源的每一种流动都有对应的轨道。5. 常见问题与排查技巧实录5.1 Git LFS 相关的问题处理拉取时提示 LFS pointer 没有下载成功怎么办常见原因是本地 Git LFS 未安装或未执行git lfs install排查时用git lfs env查看当前环境确认 filter 配置正确。如果拉下来的文件是带内容哈希的一小段文本而不是二进制本体说明本地没有完成 LFS 拉取。解决办法是执行git lfs pull手动拉取。LFS 存储配额超了怎么办主流 Git 服务商都有 LFS 流量和存储限制超了之后团队成员无法推送新的 LFS 文件。我的经验是两个方案二选一要么清理历史 LFS 文件用git lfs prune清理本地孤悬对象要么把超大的设计源文件直接移到对象存储不要放在 Git LFS 里。设计源文件的特点是最终稿通常只需要保留当前版本变更历史没有太大意义与 LFS 的历史版本记录机制并不匹配。所以我后来的建议是超过 200MB 的设计源文件直接走对象存储LFS 只管 50MB 到 200MB 的文件。5.2 命名与目录规范问题历史遗留文件太多怎么办我不建议一次性全量重命名因为大范围改动会让 Git 历史无法追踪且 review 工作巨大。我采用的做法是两步走先把存量文件整体移到archive/目录任何业务代码都不能再引用这个目录下的文件然后从当下开始严格要求新增文件符合规范这样规范迁移在一个版本内完成风险可控。模块目录需要调整怎么办目录调整也会导致代码中引用路径全部失效。正确做法是保留一个deprecated/目录放旧路径的软链或说明文件然后在下一个大版本统一删除这样既保持结构清晰也不会破坏存量开发流程。5.3 协作出错场景与排查思路场景一图片被覆盖了。排查手段是进入版本历史查看该文件的提交记录找到改动者和改动时间如果之前的版本是需要的直接 revert 提交即可。场景二线上配置 JSON 读到的是旧数据。这种问题 90% 是资源与代码版本不同步排查思路是比对构建产物包中的资源版本标记和当前 Master 的 tag定位是漏线、分支错还是缓存问题。场景三资源文件重复导致包体积变大。排查手段是写脚本遍历资源目录生成 MD5 校验表找出重复文件再通过 MR 统一清理。我分享一个印象最深的排查过程。有一个版本上线后活动页的 banner 图在公司内网测试完全正常一到生产环境就变旧图。折腾了两天最后发现是 CDN 缓存 TTL 时间设成了 7 天而资源更新后的版本号没有变化CDN 不会重新回源。从那以后我们的资源目录规范里多了一条写死的规定任何内容变更都必须修改文件名中的版本号这样既能绕过缓存也能保证历史版本可回溯。这个规定之后帮我们避免了大大小小十几次故障。5.4 团队落地过程中的实际阻力落地过程中遇到的最大阻力往往是设计师和产品人员对流程两字的反感。他们觉得流程是束缚创作自由。我的处理方式是在培训时重点讲清楚这套流程能帮他们节省什么时间不用再发 N 个版本的文件给开发不用再回答哪个才是最新版不用再在发布前反复确认素材有没有丢。事实也确实如此一般在流程运行两到三个版本迭代后反对声就消失了。如果团队实在不愿意用 Git 客户端我会统一给非开发人员配一个 GUI 工具比如 Sourcetree 或 GitHub Desktop配合简单文档让他们只做提交到指定分支这一件事。要记住流程设计的目标不是炫技而是用最小的学习成本换最大的一致性收益。6. 复盘与进阶一个不断进化的管控体系资源文件管控流程不是写完规则就结束了它必须跟着项目形态一起迭代。我一般每个季度做一次复盘看三个指标资源目录体积增长速度、违规提交拦截次数和团队对流程的满意程度。如果体积增速高说明规范有漏洞或 LFS 策略不合理如果拦截次数高说明培训和工具还不够顺手如果满意度下降说明流程繁琐需要简化环节。我特别喜欢的一个比喻是把资源文件管控当成垃圾分类。分类做得好后续的一切处理都高效顺畅分类做不好垃圾混在一起后续要花几十倍成本去修复。很多团队忽视这一点结果进入资源一塌糊涂 → 花两周清理 → 再过半年又一塌糊涂的死循环。而每次清理消耗的都是最高薪的工程师时间成本远比一开始花几天立规矩高得多。根据我个人的项目经验从 0 到 1 搭建这套流程如果项目团队小于 10 人两到三个工作日就能完成全部初始化如果能与开发节奏同步推进通常两个迭代周期内团队就能进入良性循环。关键在于启动时的坚持和自动化的充分投入而不是后面靠人肉盯防。