背景
最近遇到一个需求,需要将代码库A中的某个文件夹代码迁移到代码库B中,并且保留代码原来的提交记录。如果直接复制黏贴,代码的最后提交记录都会变成自己,并且历史的提交记录也会丢失,不符合要求。这里就来讲讲如何使用 git-filter-repo 来平滑的迁移代码
安装
git-filter-repo 是基于 python 的工具,所以我们必须要先安装 python 才能使用,可以到 python 的官网下载,它会自动检测当前的系统,直接点击下载即可
下载安装完成之后可以在终端中输入以下命令来查看是否已经安装完成:
python --version
// 或者
python3 --version
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
如果输出的是 python 的版本号,则说明已经安装成功
下载 git-filter-repo
接下来直接使用 python 的 包安装指令 pip 来下载 git-filter-repo 即可:
pip install git-filter-repo
// 或者
pip3 install git-filter-repo
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
如果是在 Mac 上也可以直接使用 brew 下载:
brew install git-filter-repo
迁移
例如我们现在有两个代码库:git-filter-repo-test1 和 git-filter-repo-test2 (下面由 代码库A 和 代码库B 替代),需要将 代码库B 中的 page2 整个文件迁移到 代码库A 代码库中,且要保留该文件的历史提交记录
首先我们先拉取一个 代码库A 代码库副本,因为迁移的过程中需要将其他无关的代码剔除,同时在迁移的过程中如果出现其他的情况,也能保证不影响原代码
提升代码到根目录
如果项目比较复杂,需要迁移的代码文件路径较深,可以先将代码提升到根目录,可以使用 git-filter-repo 提供的方法实现:
// git-filter-repo --subdirectory-filter 文件路径
git-filter-repo --subdirectory-filter src
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
如果你不是在仓库的副本上执行,那么可能会报一下的错:
这是因为如果你在一个非干净的仓库(即已有提交历史的本地仓库)上运行了 git-filter-repo,它会拒绝继续执行,这时候你可以新克隆一个仓库副本,或者直接加上 –force 来强制执行
执行完成时候,可以看到仓库 src 下的文件都被提升到了根目录
过滤需要的代码
由于我们只需要迁移 page2 文件下的代码,所有还需要将其单独拉出来,这时候就可以使用 git-filter-repo 的另一个方法来实现
// git-filter-repo --path 文件 --path 文件 可以多个
git-filter-repo --path page2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
执行完之后可以看到,代码库中就只剩下 page2 一个文件了
重命名文件名
接下来就简单了,只需要将代码推送到目标代码库 代码库A 的某个分支,然后再合并到主分支即可,但是还有一个问题,如何将 page2 文件放到 代码库A 代码库中的 page 文件夹下呢?其实只需要将现在 代码库B 中剩余的 page2 文件路径修改成和 代码库A 一致就行,也就是需要在 page2 外层在套一个 page 文件即可
这时候就可以使用 git-filter-repo 重命名文件名的方法:
// git filter-repo --path-rename <旧路径>:<新路径>
git filter-repo --path-rename page2:page/page2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
可以看到现在 page2 文件外套了一个 page
如果目标项目需要特定目录结构时,都可以使用这个方法
合并代码
接下来就是最后一步了 合并代码,我们要在 代码库B 的代码库添加 代码库A 的代码库地址,以便于将代码提交到 代码库A 代码库中
这里将 代码库A 命名为 target
git remote add target 代码库A地址
之后将代码提交到 代码库A 的某个分支上
git push target 本地分支:目标分支
最后只需要回到 代码库A 中执行合并动作即可
git merge origin/page2-merge
这时候会发现无法合并,提示 fatal:refusing to merge unrelated histories,拒绝合并无关的历史
通常情况下,Git 假设两个分支或代码库之间有共同的祖先(即共享的提交历史),因此在合并时可以找到一个共同的基点,方便进行合并操作。如果两个代码库没有任何共同的历史,Git 会认为它们是完全独立的项目,直接拒绝合并操作
这时候只需要在后面加上 –allow-unrelated-histories 即可,之后就可以看到 page2 文件被合并进来了,并且历史的提交记录也在