在大型Monorepo中,rsync仅同步变动子模块的核心是先由Git或构建工具判断哪些子模块变更,再将路径传给rsync精准执行;需用带尾斜杠路径、-avzP--delete及--exclude等参数确保增量同步与结构一致。

在大型 Monorepo 架构中,用 rsync 只同步变动的子模块,核心不是靠 rsync 自动识别“哪些子模块变了”,而是**把“子模块是否变动”这个判断逻辑前置,再交给 rsync 精准执行**。rsync 本身不读 Git 状态、不解析 workspace 配置,但它能极高效地传输你指定的、真正有差异的路径。
先确认哪些子模块实际发生了变更
Monorepo 中子模块(如 packages/foo、libs/bar)是否改动,需依赖版本控制或构建系统输出。最常用且可靠的方式是结合 Git:
- 用 git diff --name-only HEAD~1 -- packages/ 查出最近一次提交中修改过的子模块目录
- 更稳妥的做法是基于当前分支与主干(如 main)比对:git diff --name-only origin/main -- packages/ | grep '^packages/[^/]\+/$',过滤出顶层子目录
- 若使用 Nx、TurboRepo 或 Lerna,可直接调用其变更检测命令,例如:npx nx affected:apps --plain 或 turbo run build --since=main --dry-run,再提取输出中的路径
用 rsync 同步指定子模块目录(保留结构 & 增量)
拿到变动子模块列表(比如 packages/a、packages/b、libs/core)后,用 rsync 逐个或批量同步。关键点在于路径写法和参数组合:
- 每个子模块都以 带尾部斜杠的路径 传入,确保同步内容而非目录本身:packages/a/ 而非 packages/a
- 使用 -avz --delete 保证目标端与源端完全一致(删掉旧文件、保留权限时间戳、压缩传输)
- 若需排除子模块内无关内容(如 node_modules、dist、.git),加 --exclude,例如:--exclude='node_modules/' --exclude='dist/' --exclude='.git/'
- 推荐加上 -P:显示进度 + 断点续传,尤其适合 CI/CD 中不稳定网络环境
自动化脚本示例(Shell)
把上面两步串起来,一个轻量脚本就能跑通:
# 获取变动的 packages 目录(只取顶层) CHANGED_PKGS=$(git diff --name-only origin/main -- packages/ | sed 's|/.*||' | sort -u) <h1>同步每个变动子模块</h1><p>for pkg in $CHANGED_PKGS; do [[ -d "packages/$pkg" ]] || continue rsync -avzP --delete \ --exclude='node_modules/' \ --exclude='dist/' \ --exclude='.git/' \ "packages/$pkg/" \ user@server:/opt/monorepo/packages/$pkg/ done
进阶:避免重复扫描,用 rsync 的 --files-from 集中调度
当变动子模块较多(>10 个),或需统一控制排除规则时,可生成临时文件列表:
- 把所有要同步的子模块路径(带斜杠)写入 changed-list.txt,每行一个,如:packages/a/、libs/utils/
- 配合 --files-from 和 --relative 选项,保持目录层级:rsync -avzP --delete --files-from=changed-list.txt --relative . user@server:/opt/monorepo/
- 注意:--relative (-R) 会保留源路径前缀,所以源目录必须是项目根目录(.),否则路径会错位
本质上,rsync 在 Monorepo 场景里是“执行者”,不是“决策者”。它不关心你是 Lerna 还是 pnpm workspace,只认路径和文件差异。把变更检测交给 Git 或构建工具,再让 rsync 精准搬运,既稳定又高效。

















