git subtree split 生成空提交是因为路径不匹配或历史中子目录未被完整提交;它从历史扫描含指定路径的提交重写新分支,而非提取当前工作区。

git subtree split 为什么只生成空提交
执行 git subtree split 后看不到文件变更,甚至 git log 显示提交里 tree 为空——大概率是路径没对上,或者历史中该子目录根本没被完整提交过。
关键点:subtree split 不是“提取当前工作区”,而是从提交历史里扫描所有含指定路径的 commit,并重写成一条干净的新线。如果某次提交里 src/utils/ 被删过、改名过、或压根没进过暂存区,split 就会跳过它。
- 确保路径以
/开头且和实际目录结构完全一致(比如子目录叫packages/core,就用--prefix=packages/core/,末尾斜杠不能少) - 先跑
git log --oneline --follow -- packages/core/看这个路径是否真有连续提交历史;如果输出为空,说明 Git 根本没把它当独立路径追踪过 - 如果目录是后来才挪进去的(比如从别处 cp 进来但没
git add),需要先补一次全量提交:git add packages/core && git commit -m "chore: add core package"
拆分后 push 到新远程仓库失败:non-fast-forward 拒绝
常见于第一次 push 到一个刚建的空仓库,Git 报错 ! [rejected] master -> master (non-fast-forward)——这不是权限问题,是本地分支没有设置 upstream,也没初始化远程引用。
正确做法不是强制 push,而是让本地分支“认领”远程空仓库的起点:
- 先
git remote add origin-new <new-repo-url> - 再
git push origin-new <split-branch-name>:main(注意用冒号显式指定远程分支名,别依赖默认) - 如果远程是全新仓库且没任何 commit,Git 会自动创建
main分支;如果已存在默认分支(如master),就把main换成对应名 - 后续想直接
git push,得先git branch --set-upstream-to=origin-new/main
拆出来的仓库缺少 .gitignore 和 README,怎么保留原项目配置
git subtree split 只处理指定 --prefix 下的文件,不会把根目录的 .gitignore 或 README.md 带过去——这其实是设计使然,不是 bug。
GInstall OneClick — 一键安装 GitHub 项目:规划、克隆、安装 Node 依赖、运行开发脚本。适用于想要获取 GitHubNode.js 项目的用户。
如果你希望新仓库开箱可用,得手动补:
- 拆完后进新仓库目录,
cp /path/to/original/.gitignore ./(注意别复制错路径) - 同理复制
README.md,但建议重写开头,注明这是从原项目packages/core/拆出的独立模块 - 别忘了补
package.json的"name"和"publishConfig"字段(如果是 npm 包),否则发布时会撞名 - 如果原项目用了 Lerna 或 Turborepo,拆出来后要删掉
lerna.json或turbo.json相关配置,否则本地命令会报错
用 subtree split 后,原仓库还能正常合入更新吗
能,但必须用 git subtree push,不能直接 git push ——因为拆出的分支在原仓库里只是普通 commit,没关联远程。
一旦你用 git subtree split -P packages/core/ -b core-split 创建了 core-split 分支,后续往新仓库提交的改动,就得靠 subtree push 拉回来:
- 先在原仓库 fetch 新仓库:
git remote add core-remote <new-repo-url>,然后git fetch core-remote - 再执行
git subtree push --prefix=packages/core/ core-remote main(注意--prefix必须和当初 split 时一致) - 如果提示 “Can't push non-subtree commit”,说明你当前分支没继承自当初 split 出来的那个 commit,得先
git merge或git rebase对齐历史 - 每次 push 都会生成一个合并提交,原仓库的
packages/core/目录内容会变成新仓库最新状态
真正容易被忽略的是:subtree 的双向同步不是自动的,也没有冲突自动解决逻辑。改同一文件的同一行,push 时就会卡住,得手动 git checkout -p 挑选 patch,或者删掉旧 subtree 再重新 merge。

















