GeneralAndroid 大文件迁移方案(GitLab → MinIO)

1. 背景与目标

当前 GeneralAndroid 仓库已经出现明显的大文件污染问题,主要表现为:

  • 仓库工作区总大小约 31.10 GiB
  • .git 目录大小约 15.43 GiB
  • Git 跟踪文件总大小约 12.87 GiB
  • Git 跟踪前 40 大文件合计约 8.43 GiB,占跟踪文件总体积 65.51%

这说明仓库不仅当前文件体积偏大,而且 Git 历史对象中已经累积了大量超大二进制文件。这会直接带来:

  • git clone / git pull 容易超时、中断、EOF
  • 仓库同步速度明显下降
  • GitLab 服务端传输对象负担过重
  • 本地 .git 持续膨胀,维护成本越来越高

本方案的目标是:

  1. 将大文件从 Git 仓库中迁移到已部署的 MinIO
  2. 让 Git 仓库回归“源码仓库”定位
  3. 建立大文件后续托管规范,防止再次污染
  4. 在条件成熟后,清理 Git 历史中的大对象,根治 clone/pull 问题

2. 总体方案

已确定采用以下职责划分:

  • GitLab:仅存放源码、脚本、文档、配置、少量必要小样本
  • MinIO:存放大文件资源,包括发布包、视频、trace、书籍、工具二进制
  • 仓库内清单文件:记录 MinIO 上的资源位置、校验值、用途说明、下载方式

同时约定:

  • assets/resource_manifest.json 是权威数据源
  • assets/resource_manifest.mdassets/assets_sha256.txt 都由脚本根据 JSON manifest 派生生成,不作为手工维护入口
  • 上传脚本只有在 mc stat 确认远端对象存在且 size 符合预期后,才允许写 manifest / SHA / history

2.2 远程 clone 与恢复主路线

当前主路线已经明确为:

  • 远程 clone / 资产恢复优先走公网 mc endpoint:http://tx2.898311.xyz:9010
  • 通过环境变量 GENERALANDROID_MC_ALIASGENERALANDROID_MC_ENDPOINTGENERALANDROID_MC_ACCESS_KEYGENERALANDROID_MC_SECRET_KEYscripts/bootstrap_repo.sh 自动创建 alias
  • scripts/download_assets.py / scripts/upload_assets.py 保持兼容现有 local alias,本机工作流不移除
  • 预签名下载保留为后续增强路线,不作为当前主流程前置条件

这意味着阶段二的长期管理方式,已经从“手工预配 local alias”调整为“公网 mc + 受限账号 + bootstrap 自动配置”。

  • generalandroid/
    • releases/
    • media/
    • traces/
    • tools/
    • books/

分类规则:

  • releases/:zip、tar.gz、7z、apk、完整发布包、离线分发包
  • media/:webm、mp4 等录屏/培训/分享视频
  • traces/:atrace、perfetto、hprof、bugreport 等分析数据
  • tools/:so、a、jar、独立工具二进制、工具链文件
  • books/:pdf、电子书、参考资料

3. 迁移原则

3.1 必须迁移的对象

以下类型默认不应继续留在 Git 主仓库中:

  • .zip .tar.gz .7z
  • .webm .mp4
  • .pdf
  • .hprof .atrace .perfetto-trace
  • 超大 .so .a .jar
  • 编译产物
  • 第三方工具二进制
  • 可通过脚本重新下载或生成的大样本文件

3.2 可保留在 Git 中的对象

仅保留以下内容:

  • 源码
  • 配置文件
  • 文档
  • 小型示例文件
  • 必须纳入版本控制且体积很小的测试样本
  • 经明确确认、当前阶段暂不迁移的少数大文件

当前已确认的例外:

  • 当前无例外项,原先暂不迁移的 main/hideapi/frida/frida-core-devkit-17.2.12-android-arm64/libfrida-core.a 已改为正式迁移,并已上传到 MinIO。

3.3 仓库中不再保留本体,只保留索引

迁移后,仓库中保留:

  • 资源清单(manifest)
  • SHA256 校验信息
  • 下载脚本
  • 上传脚本(可选)
  • 资源使用说明

不再保留大文件本体。


4. 分阶段实施方案

阶段一:止血(立即执行)

目标:停止继续向 Git 提交大文件。

动作:

  1. 根据当前大文件报告,识别需迁移文件
  2. 将这些文件上传到 MinIO
  3. 在仓库中建立资源清单
  4. 从 Git 当前版本中删除这些大文件
  5. 补充 .gitignore 规则,防止再次提交

阶段一效果:

  • 后续提交不再继续恶化
  • Git 当前分支体积增长被控制
  • 开发流程恢复可维护状态

注意:

  • 阶段一 不能缩小已有 Git 历史体积
  • 只能阻止问题继续扩大

阶段二:规范化(紧随阶段一)

目标:建立长期可维护的大文件管理方式。

动作:

  1. 固化 MinIO 目录规范
  2. 固化仓库资源清单格式
  3. 增加下载脚本
  4. 视需要增加上传脚本
  5. 在团队内约定:大资源一律不直接进 Git

阶段二效果:

  • 仓库和大资源彻底解耦
  • 新资源都有固定归档位置
  • 后续维护成本大幅下降

阶段三:历史治理(择机执行)

目标:真正缩小 Git 仓库体积,根治 clone/pull 问题,让后续新同事重新 clone 时尽可能接近“最小下载”。

动作:

  1. 完成未来治理闭环(即 >40MB 文件全部走 MinIO,禁止继续把新大文件写入 Git)
  2. 根据迁移映射清单和 MinIO manifest,确认历史中要剔除的大文件都已有可恢复落点
  3. 对当前仓库做完整备份(裸仓库备份 + 工作区备份)
  4. 使用 git filter-repo 或 BFG 清理历史大文件
  5. 本地执行 git gc / git repack,验证瘦身结果
  6. 在维护窗口强制推送到 GitLab
  7. 所有使用者重新 clone 或按新方式同步
  8. 用全新 clone 做一次最小下载验证

阶段三效果:

  • .git 体积显著下降
  • clone / pull 恢复正常
  • GitLab 服务端传输负担下降
  • 新同事重新 clone 时,不再被旧历史大对象拖累

注意:

  • 该阶段属于高风险操作
  • 会改写提交历史
  • 必须在确认窗口期、完成备份、确认使用者同步策略后执行
  • 只有阶段三完成后,重新 clone 才能真正接近最小下载

4.3.1 阶段三的实施前提

在历史清理开始前,必须满足以下前提:

  1. MinIO 上传 / 下载 / manifest / hook / GitLab CI 流程已经稳定
  2. 需要保留的大文件已经迁移到 MinIO,且可通过脚本恢复
  3. 暂不迁移例外项已经单独确认(当前例外:main/hideapi/frida/frida-core-devkit-17.2.12-android-arm64/libfrida-core.a
  4. 团队已经接受“重写历史后需要重新 clone”的切换方式
  5. 已确定维护窗口,避免在活跃开发期直接改写远端历史

4.3.2 阶段三的推荐执行顺序

建议严格按以下顺序执行:

  1. 冻结新增大文件提交
  2. 再次导出仓库大文件清单,确认要剔除的对象集合
  3. 核对 MinIO manifest,确保对应对象可恢复
  4. 创建本地裸仓库备份
  5. 创建工作区备份
  6. 使用 git filter-repo / BFG 清理历史
  7. 本地重新统计:
    • git count-objects -vH
    • du -sh .git
  8. 用全新临时目录重新 clone 本地清理后的仓库,验证:
    • clone 时间
    • .git 体积
    • 下载脚本恢复能力
  9. 确认无误后再推送远端
  10. 发布团队切换说明,要求全员重新 clone

4.3.3 历史清理完成后的验收标准

只有以下全部满足,才算“最小 clone 改造完成”:

  1. 新 clone 的仓库 .git 体积明显低于当前状态
  2. clone 过程中不再因为历史大对象频繁超时/EOF
  3. 已迁移文件可从 MinIO 恢复
  4. 本地 hook 和 GitLab CI 仍能阻止新的 >40MB 文件直接进入 Git
  5. 团队成员按照新流程重新 clone 后可正常开发

4.3.4 对其他 AI / 测试人员的要求

如果后续由其他 AI 或其他人员负责阶段三验证,应要求其至少完成以下检查:

  1. 记录历史清理前后的:
    • du -sh .git
    • git count-objects -vH
    • clone 耗时
  2. 在一个全新目录执行 clone,不能直接复用旧工作区
  3. clone 后立刻验证:
    • manifest 是否存在
    • 下载脚本是否可恢复已迁移资源
  4. 验证一个违规 >40MB 文件提交仍会被 hook / CI 拦截
  5. 输出一份“历史清理前后对比报告”

5. 迁移对象分类规则

后续生成详细迁移清单时,按照以下规则归类:

5.1 releases/

适用于:

  • tool_new.zip
  • 各类发布包
  • 归档安装包
  • render_doc_hook.zip
  • 其他 zip/tar.gz/7z 成品包

5.2 media/

适用于:

  • .webm
  • .mp4
  • 录屏、课程、分享视频

5.3 traces/

适用于:

  • .atrace
  • .hprof
  • bugreport
  • perfetto 数据
  • 分析样本

5.4 tools/

适用于:

  • .so
  • .a
  • 大型 .jar
  • 独立二进制工具
  • 第三方工具链

5.5 books/

适用于:

  • .pdf
  • 电子书
  • 技术资料附件

6. 建议的仓库改造内容

6.1 建议新增的清单文件与脚本

建议在仓库中新增以下文件:

  • assets/resource_manifest.json(主清单)
  • assets/resource_manifest.md(阅读视图,由 JSON 派生)
  • assets/assets_sha256.txt(派生校验文件)
  • assets/asset_history.jsonl(生命周期操作历史)
  • scripts/upload_assets.py
  • scripts/batch_upload_assets.py
  • scripts/download_assets.py
  • scripts/list_assets.py
  • scripts/update_asset.py
  • scripts/remove_asset.py
  • scripts/bootstrap_repo.sh
  • scripts/install_git_hooks.sh
  • .claude/commands/clone-init.md

6.2 清单文件职责

当前文件职责必须明确区分:

  • assets/resource_manifest.json
    • 主清单
    • 唯一权威数据源
    • 上传 / 下载 / 查询 / 更新 / 删除 / 校验脚本都以它为准
  • assets/resource_manifest.md
    • 阅读视图
    • 由 JSON 自动派生
    • 不手工维护
  • assets/assets_sha256.txt
    • 派生校验文件
    • 由 JSON 自动派生
    • 不手工维护
  • assets/asset_history.jsonl
    • 生命周期历史记录
    • 只追加,不回写旧记录

因此当前不应再把 resource_manifest.md 视为主清单。

6.3 下载脚本职责

下载脚本负责:

  • 按资源清单下载所需文件
  • 校验 SHA256
  • 放回约定本地路径
  • 支持按分类下载,例如:
    • --media
    • --tools
    • --traces
    • --all
  • 支持 --jobs 并发下载
  • 支持 --continue-on-error,单文件失败时继续处理剩余任务
  • 支持 resumable rerun:已存在且 size + SHA256 校验通过的本地文件直接跳过
  • 下载前先对远端对象执行 mc stat --json,至少确认对象存在且 size 与 manifest 一致

6.4 .gitignore 建议方向

后续应把明显的大文件类型或产物目录加入 .gitignore,例如:

  • 打包产物目录
  • trace 输出目录
  • hprof 输出目录
  • 大型 zip/tar.gz/7z 产物目录
  • 工具构建输出目录

注意:

  • .gitignore 不能解决历史污染
  • 只能阻止后续重复提交

7. 执行顺序建议

建议采用以下顺序:

  1. 固化 MinIO 目录规范(已确定)
  2. 根据前 40 大文件生成详细迁移清单
  3. 逐批上传到 MinIO
  4. 生成并补充仓库资源清单与校验文件
  5. 删除 Git 当前版本中的大文件并补 .gitignore
  6. 验证本地脚本可以按需拉取资源
  7. 对批量迁移场景优先使用并发上传脚本:worker 只负责 mc cp + mc stat 确认,manifest / SHA / history 统一由主线程串行落盘
  8. 选择维护窗口执行历史清理

不建议的顺序:

  • 先做历史重写,再决定资源怎么放
  • 先删仓库大文件,但没有清单和下载入口
  • 没有备份就直接改写历史

8. 风险与注意事项

8.1 当前版本删除 ≠ 历史清理

从当前分支删掉大文件,只会减少后续维护压力,不会自动缩小 .git 历史

8.2 历史重写影响所有使用者

一旦用 git filter-repo / BFG 清历史:

  • 所有人本地分支都可能需要重新同步
  • 旧 commit hash 会变化
  • 需要明确切换窗口

8.3 迁移前必须保证资源可追溯

迁移后不能出现:

  • 只知道文件迁走了,不知道放哪
  • 没有 SHA256
  • 没有下载脚本
  • 文档引用失效

8.4 MinIO 权限要先想清楚

至少要明确:

  • 是仅局域网访问还是公网访问
  • 是否允许匿名下载
  • 是否只允许登录后访问
  • 文档中记录的链接是否长期有效

9. 推荐最终状态

理想完成态如下:

Git 仓库中

只保留:

  • 源码
  • 文档
  • 配置
  • 少量小样本
  • 资源清单
  • 下载脚本

MinIO 中

统一托管:

  • 大型发布包
  • 视频资料
  • trace / hprof / bugreport
  • 大型工具链文件
  • PDF / 书籍资料

Git 历史

经过清理后:

  • 不再包含超大 zip、视频、trace、工具包等历史对象
  • clone/pull 大幅恢复正常

11. 大文件资源的长期维护方式

后续大文件不再按“Git 直接版本化文件本体”的方式维护,而采用:

  • Git:维护资源索引和元数据
  • MinIO:维护资源文件本体

11.1 增删改查原则

新增(增)

新增大文件时:

  1. 执行上传脚本
  2. 上传文件到 MinIO
  3. 自动更新 manifest / sha256
  4. 提交元数据文件,不提交大文件本体

删除(删)

删除大文件时必须同时处理:

  1. Git 中的 manifest 条目
  2. MinIO 中的对象

禁止只删一边。

修改(改)

修改大文件时,不建议原地覆盖,而应:

  1. 上传新对象
  2. 更新 manifest 指向
  3. 更新 sha256 / size / uploaded_at
  4. 提交 manifest 变更

查询(查)

查询需要分别从两层进行:

  1. Git 层:
    • resource_manifest.json
    • resource_manifest.md
    • Git 提交历史
  2. MinIO 层:
    • mc ls
    • mc stat

11.2 增删改查脚本落地情况

当前已实现:

  • scripts/upload_assets.py:新增并登记大文件
  • scripts/download_assets.py:从 MinIO 恢复已登记资源
  • scripts/list_assets.py:列出 / 查询当前已登记资源
  • scripts/update_asset.py:更新已登记资源的新版本
  • scripts/remove_asset.py:删除 manifest 条目并删除或保留 MinIO 对象

同时已引入资产历史记录:

  • assets/asset_history.jsonl

作用:

  • 记录 upload / update / remove 操作
  • 用 Git 保留操作历史
  • 支撑单文件生命周期追踪和问题排查

建议统一通过这些脚本维护大文件,不要手工改 manifest,也不要手工直接删 MinIO 对象后忘记同步 Git 元数据。

11.3 远程同事 / 非局域网环境说明

当前脚本默认调用 mc alias local,这只是一个默认约定,不代表只能在局域网环境下使用。

只要使用者机器上的 mc alias 指向一个其网络可访问的 MinIO/S3 兼容入口,脚本就能继续工作。

这意味着:

  • 局域网同事可以使用本地 / 内网 MinIO 入口
  • 非局域网同事如果能访问公网 MinIO 域名,也可以工作
  • 如果飞出局域网后无法访问目标 MinIO 地址,则上传/下载脚本无法正常使用

因此,对远程同事真正的约束不是脚本本身,而是:

  1. MinIO 服务是否可达
  2. mc alias 是否已正确配置
  3. 账号凭证是否可用
  4. 网络速度是否可接受

后续如需更好支持远程同事,可继续增强:

  • 支持通过环境变量配置默认 alias
  • bootstrap 增加 alias 选择能力
  • 文档中拆分内网 / 外网初始化说明

11.4 下一阶段实施内容

当前状态下,“TOP40 范围内对象迁移到 MinIO”已经完成,下一阶段重点不再是继续补迁,而是:

  1. 分批从当前仓库版本中删除已迁移大文件本体
  2. 持续验证 bootstrap / 下载脚本恢复链路
  3. 继续验证远程同事 / 外网 MinIO API 访问方式
  4. 准备历史清理执行清单与维护窗口

完成后即可形成完整的“增删改查 + MinIO 同步维护 + 当前版本瘦身 + 历史清理准备”闭环。