先问你一句:你是真的需要“跨仓库提交迁移”,还是只是要把整个仓库换个地方托管?这两个问题看起来像,实际操作完全是两码事。
如果在代码仓库从单体拆分、服务独立交付、仓库合并、平台切换这类场景里待过,大概率会碰到一种需求:把仓库 A 的某一部分代码,连同它的完整提交历史,搬进仓库 B。很多人第一反应是“把代码复制过去再重新提交一次”,但这样做历史就没了;懂一点的会想到 git filter-branch,但用起来极其难受,还容易把仓库搞坏。今天这篇我就围绕 Git 的跨仓库提交迁移,把背后的对象模型、常用工具、梳理过的实操案例和一些踩坑经验一次性讲清楚。
这篇文章适合谁?想拆分 monorepo 的后端开发、要整理仓库边界的架构师,以及被老板要求“把支付模块历史搬到新仓库,但一条提交都不能少”的运维或全栈同学。文章核心不算短,但我尽量把每一步为什么这样做讲明白,而不是丢给你一堆跑不清楚的命令。
1. 先分清你的迁移层级:空仓库整体迁,还是让 Git 改写历史
跨仓库提交迁移的第一课不是学命令,而是判断需求属于哪一层。根据我的经验,至少三分之一的人根本不需要做“历史改写”,只是在托管平台间搬家而已。搞清楚这一点,能帮你省下几个小时,也避免在不该重写历史时把全部提交哈希搞乱。
1.1 目标仓库是空仓时,搬运即可,不用重写
如果旧仓库整体迁移到新地址,新仓库是空的,且你要保留分支、标签、全部提交,那直接搬运对象就行。Git 的提交对象本身是可移植的,commit、tree、blob 这些对象从一台服务器传到另一台,只要内容没被改动,哈希就不会变。很多人误以为“换仓库就等于 SHA 一定变”,其实不是,迁移只是对象被原封不动地搬走。
最简单的操作是 --mirror:
bash复制git clone --mirror git@git.example.com:old/legacy-project.git
cd legacy-project.git
git push --mirror git@git.example.com:new/legacy-project.git
用 mirror 而不是普通 clone 再 push,原因在于 mirror 会把远端所有引用,包括分支、tag、远程跟踪分支,都完整地复制下来。普通 clone 默认只带当前分支,给目标仓库留下一个“缺胳膊少腿”的隐患。
如果新旧两台服务器之间不能直接网络访问,也可以用 bundle 文件做搬运:
bash复制git bundle create legacy-project.bundle --all
把生成的文件传到目标机器后:
bash复制git clone legacy-project.bundle
cd legacy-project
git remote set-url origin git@git.example.com:new/legacy-project.git
git push --mirror origin
bundle 本质是一个 Git 对象数据库的打包文件,恢复出来的仓库对象和原仓库一致,提交哈希也不会变。这一步就完成了“换地址”,根本不需要碰历史内容。
1.2 需要“切”历史的场景,从一开始就要有成本预期
真正困难的是另一类需求:不是搬整个仓库,而是对历史做筛选和改造。常见的长这样:
- 把 monorepo 里的
services/payments目录抽成独立仓库,保留这笔目录相关的所有提交; - 把一段只包含某几个分支的历史,合并进一个没有任何关联的已有仓库;
- 迁移的同时需要批量删除历史中的大文件、密钥或误提交的敏感信息;
- 把两个产品的仓库合并为一个仓库,保留两边各自的提交人、提交时间和 commit message。
这类需求无法用“复制粘贴”或“mirror 推送”完成,必须让 Git 按规则重写历史。一旦重写,从第一个被修改的提交开始,后续所有提交的哈希都会像多米诺骨牌一样全部改变。这还不是最麻烦的,真正麻烦的是所有旧 clone 都会失效,团队必须接受一次“重置式”的切换,期间如果有人基于旧历史继续提交并推回新仓库,之前做的清洗就可能白费。
所以在开始前,先和团队对齐三点:提交哈希会变,外部系统如果拿 SHA 做关联需要准备映射表;需要一个仓库冻结窗口,不能边迁移边开发;迁移后所有成员必须重新 clone,旧 clone 不能再往新仓库推。这三点确认完,再动手。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 先把哈希链条想明白:为什么提交历史不能像压缩包一样随手搬
我刚工作那会儿也犯过嘀咕:Git 的提交历史不就是一堆 diff 叠在一起吗?那我把它按顺序重放一遍不就行了?后来才发现,这个理解错得离谱。提交历史不是 diff 列表,而是一棵由哈希串联起来的对象图。理解这个,才算真正理解“提交迁移”背后的代价。
