免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

Flutter Monorepo 合并实战:用 git filter-repo 完成 Engine 历史剪枝、目录重写与仓库合并

Flutter Monorepo 合并实战:用 git filter-repo 完成 Engine 历史剪枝、目录重写与仓库合并 Flutter Monorepo 合并实战用 git filter-repo 完成 Engine 历史剪枝、目录重写与仓库合并【免费下载链接】flutterFlutter makes it easy and fast to build beautiful apps for mobile and beyond项目地址: https://gitcode.com/GitHub_Trending/flutter41/flutterFlutter 在将flutter/engine仓库合并进flutter/fluttermonorepo时面临一个典型的大仓库治理问题如何在尽量保留有用历史的前提下把.git体积控制在可接受范围内。本文基于仓库中的 历史剪枝策略文档完整还原这次合并的工程操作从安全准备、git filter-repo历史剪枝、目录结构重写、提交信息改写到最终与 framework 仓库的合并并结合当前仓库的实际布局engine/src/flutter、根目录DEPS说明每一步的最终落地形态帮助你在处理“历史包袱过重的大型仓库”或“多仓库合并”时拥有一套可复制的操作模板。背景Engine 仓库 780MB 历史的来源合并前的 engine 仓库.git目录高达约 780MB。文档明确指出体积膨胀主要来自三类历史遗留曾经提交、但如今不再使用的二进制文件近十年前提交、后来又被移除的第三方库创建后又迁移到别处的示例工程。剪枝的总原则是尽量保留对当前 engine 开发相对有用的历史同时不让 framework 仓库的.git文件夹失控膨胀。Step 1全新克隆 安全隔离改写历史的铁律是不要在你的工作区上操作。文档给出的准备工作如下############################################## ## Do some cleanup work on the engine and get ## the folder structure right. ############################################## # clone the repo to a fresh working folder git clone gitgithub.com:flutter/engine.git engine_prep cd engine_prep # for safety - remove the remote - were going to edit history git remote remove origin关键点克隆到全新目录engine_prep并立即git remote remove origin移除远端引用——所有后续操作都是破坏性的历史改写移除 origin 可以杜绝误推到上游仓库。可选先分析仓库再动手如果想在剪枝前先摸清仓库中“谁占了大头”文档建议安装 git filter-repo 工具后运行分析# Analyze if you want, just remember to remove .git/filter-repo git filter-repo --analyze --force分析结果写入.git/filter-repo目录。Step 2 中的剪枝决策表正是基于这份分析得出的。Step 2基于分析结果剪枝历史剪枝决策表下表来自 git-filter-repo 的分析输出。Packed Size指打包后体积存在交叉引用。决策标准是挑选那些已不再被引用的大文件以及 2016 年以前的老目录。Packed SizeDeleted DatePathNotes1127847452024-05-13ci/licenses_golden/licenses_third_party27531902~2021*.jarbinary273799312016-08-09third_party/android_platformandroid_platform and webview270000002024-07-15impeller/docs/assets/*(png|gif)moved to another repository151213752023-02-13*.ttcfont files101041822023-02-13*/SourceHanSerifCN*79856822018-08-08travisold ci63156372015-11-07examples/game39394292015-07-28sky/sdk39394292015-07-28sky/packages/sky39037872016-08-09mojo36868302022-06-14testing/scenario_app/android/reports31889302015-06-30tests/fast31739662015-08-07*/example/game*20189612016-08-09third_party/libxml18041992016-08-09third_party/tcmalloc1393936~2016*.dllbinary13737402017-07-06tests/data11006652015-06-27benchmarks/parser/resources/html5.html10596732015-07-20third_party/protobuf9788702022-04-27impeller/third_party7988522015-07-20third_party/cython7785602022-01-24lib/web_ui/test/golden_files6344552016-08-09third_party/libpng6107512024-05-13.golden5504752024-09-17impeller/fixtures/flutter_logo_baked.*5268372016-08-09third_party/libevent5234362015-07-20third_party/boringssl5149682022-04-27impeller/fixtures/image.png4615272015-12-11third_party/re24181222015-10-12examples/demo_launcher4137872015-11-07.aac3627872016-08-09third_party/glfw3496042016-08-09third_party/harfbuzz-ng3408692016-08-09third_party/okhttp3216592016-08-09.S3008242016-08-09.so2576332016-08-09third_party/libjpeg2575192016-08-09third_party/jinja22496182016-08-09third_party/zlib2186432015-12-11third_party/brotli1886222021-01-06.idl1845932015-09-02third_party/khronos1732102016-08-09.gypi1704842016-08-09third_party/expat1695782016-08-09.asm1613602016-08-09.m41426702018-05-10.in1403642015-12-11third_party/ots1372702016-08-09.hh1367872016-08-09.gyp995032016-08-09third_party/qcms917302015-08-21.pxd848502016-08-09third_party/yasm执行剪枝以下命令把这些文件和目录从 checkout 历史中移除。由于这是破坏性编辑过程中所有 SHA1 提交哈希都会改变。完成后.git历史中的 object 文件约为 74MB整个.git目录从约 780MB 降到约 110MB# Lets do some heavy filtering; # .git starts out at ~780MB and ends up at ~110MB git filter-repo --force --invert-paths \ --path-glob impeller/docs/assets/*.png \ --path-glob impeller/docs/assets/*.gif \ --path-glob */example/game/* \ --path-glob benchmarks/parser/resources/html5.html \ --path-glob *.dll \ --path-glob *.jar \ --path-glob */SourceHanSerifCN* \ --path-glob third_party/txt/third_party/fonts/NotoSansCJK-Regular.ttc \ --path-glob impeller/fixtures/flutter_logo_baked.* \ --path-glob impeller/fixtures/image.png \ --path-glob *.golden \ --path-glob *.aac \ --path-glob *.S \ --path-glob *.so \ --path-glob *.idl \ --path-glob *.gpy \ --path-glob *.gypi \ --path-glob *.asm \ --path-glob *.m4 \ --path-glob *.in \ --path-glob *.pxd \ --path-glob *.hh \ --path-glob *.gyp \ --path ci/licenses_golden/licenses_third_party \ --path testing/scenario_app/android/reports \ --path impeller/third_party \ --path mojo/public/third_party \ --path tests/data \ --path tests/fast \ --path tests/framework \ --path travis \ --path mojo \ --path sky/sdk \ --path sky/engine \ --path sky/tools/webkitpy \ --path sky/shell \ --path sky/packages/sky \ --path sky/tests \ --path sky/unit \ --path sky/services \ --path sky/compositor \ --path sky/build \ --path sky/specs \ --path skysprites \ --path examples/demo_launcher \ --path examples/game \ --path third_party/qcms \ --path third_party/libevent \ --path third_party/boringssl \ --path third_party/tcmalloc \ --path third_party/cython \ --path third_party/protobuf \ --path third_party/libpng \ --path third_party/re2 \ --path third_party/harfbuzz-ng \ --path third_party/jinja2 \ --path third_party/libjpeg \ --path third_party/glfw \ --path third_party/zlib \ --path third_party/android_platform \ --path third_party/expat \ --path third_party/brotli \ --path third_party/yasm \ --path third_party/khronos \ --path third_party/okhttp \ --path third_party/libxml \ --path third_party/ots \ --path third_party/libXNVCtrl \ --path lib/web_ui/test/golden_files \ --path apk \ --path flutter \ --path base \ --path sdk \ --path gpu \ --path engine \ --path tools/webkitpy \ --path tools/valgrind \ --path tools/clang \ --path tools/android \ --path build/linux \ --path build/win \ --path build/mac \ --path ui \ --path examples/stocks \ --path examples/stocks2 \ --path examples/stocks-fn \ --path examples/data \ --path examples/fitness \ --path examples/city-list \ --path examples/widgets \ --path examples/raw \ --path examples/color \ --path examples/flights \ --path examples/rendering \ --path examples/fn \ --path specs \ --path url \ --path services \ --path framework \ --path crypto \ --path skia/ext \ --path e2etests \ --path tests/resources \ --path viewer \ --path lib/stub_ui \ --path content_handler # Garbage collect! git reflog expire --expirenow --all git gc --prunenow --aggressive几个值得注意的实现细节--invert-paths是关键开关配合一组--path-glob/--path参数语义从“只保留”反转为“剔除这些路径”因此只需列出要删的内容无需枚举全部要保留的路径剔除清单分三类按扩展名的全局二进制*.jar、*.dll、*.so、*.ttc等、特定大文件如html5.html、CJK 字体、按目录的历史包袱sky/*、mojo、third_party/*各库、examples/*老示例末尾的git reflog expire --expirenow --all git gc --prunenow --aggressive是回收空间的关键一步filter-repo只是重写了引用旧对象要靠清空 reflog 激进垃圾回收才真正从磁盘消失。Step 3重写目录结构到 engine/src/flutter合并后engine 源码的最终归宿是engine/src/flutter目录唯一的例外是DEPS它保留在仓库根目录。文档特意指出不要用git mv——它只影响 HEAD在日志追溯上会埋下隐患。正确做法是继续用filter-repo重写整段历史让“每个历史版本里的路径”在新世界里都成立# Move files to engine/src/flutter, update tags so they dont collide, and move DEPS back to root. git filter-repo --to-subdirectory-filter engine/src/flutter --tag-rename :engine- --force git filter-repo --path-rename engine/src/flutter/DEPS:DEPS三个动作各有目的--to-subdirectory-filter engine/src/flutter把全部历史整体下沉到该子目录--tag-rename :engine-给所有历史 tag 加上engine-前缀避免与 framework 仓库的 tag 命名空间冲突第二条命令把engine/src/flutter/DEPS重新挪回根目录形成最终布局。这一布局在当前仓库中可以直接验证根目录存在 DEPS头部注释说明它被根目录的.gclient文件引用、供gclient sync管理第三方依赖而 engine 源码位于 engine/src/flutter其下包含BUILD.gn、display_list、flow、impeller等构建与源码目录。配套的 engine/scripts/standard.gclient 脚本则说明了 monorepo 下的依赖同步方式将其复制到 checkout 根目录即可用gclient sync引导其中deps_file指向根目录的DEPSurl指向flutter/flutter仓库——正是 Step 3 所建立的目录契约。Step 4重写提交信息中的 PR 链接合并前还有一个细节必须处理engine 提交信息首行通常带有 PR 链接形如... (#12345)。合并后这些 PR 号属于原 engine 仓库而flutter/flutter没有同样的历史链接会指向错误的 PR。为了“让历史稍微好看一点”文档只改写提交信息的第一行并强调这一步必须在与flutter/flutter合并之前完成以免“踩到”对方的提交行。git filter-repo --force --message-callback return re.sub(br^(.*)\((#\d)\)\n(.*), br\1(flutter/engine\2)\n\3, message, 1) --message-callback暴露了一个 Python 回调对每条提交信息执行正则替换把首行的( #12345 )补全为( flutter/engine#12345 )的形式使 PR 引用带上原仓库限定前缀\n后的正文保持原样不动。最后一步与 flutter/flutter 合并准备工作完成后回到 framework 仓库执行合并############################################## ## Now handle merging into flutter/flutter ############################################## git clone gitgithub.com:flutter/flutter.git flutter_merge cd flutter_merge # add the other tree as remote git remote add -f engine-upstream ~/src/engine_prep # --no-commit is important because we want to look around git merge --no-commit --allow-unrelated-histories engine-upstream/main # Youre a wizard, Harry git commit -m Merge flutter/engine into framework # Garbage collect! # Now at 234MB .git git reflog expire --expirenow --all git gc --prunenow --aggressive要点解析git remote add -f engine-upstream ~/src/engine_prep把本地准备目录作为远端拉取-f立即 fetch而不是真的推送到 GitHub--allow-unrelated-histories是必需的剪枝重写后的 engine 历史与 framework 历史没有任何共同祖先--no-commit让合并停在暂存状态先人工检查冲突与目录落位再手动提交那条 “Merge flutter/engine into framework” 的合并提交合并完成并再次激进 GC 后framework 仓库的.git约 234MB——即 engine 剪枝贡献 framework 原有历史的总和。结果形态与工程启示从当前仓库的结构看这套策略的最终产物是目录契约engine 源码统一收敛在engine/src/flutter下DEPS留在根目录被gclient体系引用第三方依赖不再以源码形式提交到 git 历史中DEPS中通过 revision 变量引用 Skia、Dart、Chromium 等外部依赖历史可追溯但体积受控被剪掉的只是“不再被引用”的内容仍有价值的提交历史包括被重写的路径与 tag得以保留合并后整个.git约为 234MB相对合并前 780MB framework 历史的叠加体积是可控的配套机制联动目录重写还引出了合并后的新问题——framework 如何定位“哪次提交对应哪个引擎二进制”这部分由同目录的 引擎二进制哈希策略 用内容哈希git ls-tree -r HEAD engine DEPS | git hash-object --stdin解决与本文的布局约定engine/ 根DEPS严格对应。对面临类似场景的团队这套流程的可复用要点是先在全新克隆上操作并移除 origin用--analyze拿数据再定剪枝清单而不是凭感觉删目录路径剔除用--invert-paths一次性完成目录迁移、tag 命名空间、提交信息改写全部用历史重写而非 HEAD 操作保证整段历史自洽所有改写完成后再合并合并用--no-commit --allow-unrelated-histories留出人工检查窗口每一步重写后都要reflog expiregc --prunenow --aggressive回收空间。需要牢记的适用前提是历史重写会改变所有 SHA只能在“合并进目标仓库之前、且上游仓库可以接受历史被改写”的窗口内执行。【免费下载链接】flutterFlutter makes it easy and fast to build beautiful apps for mobile and beyond项目地址: https://gitcode.com/GitHub_Trending/flutter41/flutter创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表