用 git-filter-repo 分离代码到新仓库,并保留提交记录

1,942 阅读3分钟
一、背景及目标介绍

背景: 把存放于 后台仓库 的 前端代码 完整抽离为一个单独的仓库,并保留需要的分支及提交记录。

目标: 新仓库中,只包含:public目录、dist目录、package.json、package-lock.json、README.md 文件

1. 服务端代码库目录结构:

# 如:projects-backend.git
├── backend
├── projects
│  ├── ...
│  ├── public -- 要提取的文件夹
│  ├── dist -- 要提取的文件夹
│  ├── package.json -- 要提取的文件
│  ├── package-lock.json -- 要提取的文件
│  ├── README.md -- 要提取的文件
├── ...
└── ...

2. 预期实现的效果:

# 如:projects-front-end.git
├── public
├── dist
├── package.json
├── package-lock.json
└── README.md
二、大致步骤:
0. 安装 git-filter-repo         
1. 创建新的 git 仓库 备用
2. 通过设置 depth 浅 clone projects-backend 仓库 ,保留近 100次 commit (原因:仓库太大,完整拿不下来, 或完整拿下来后,push到新仓库失败)            
3. 分别 fetch 要保留的分支(因为 浅clone 下来的仓库,只有 master 分支的代码,想要其他的分支,需单独做分支拉取操作)            
4. 使用 --subdirectory-filter,把 public、dist 等前端文件和文件夹提取到第一级(根目录),去除 projects 这层目录
5. 使用 filter-repo --path 拿取我们想要的文件和文件夹
6. 将 remote origin 更改为 新仓库的地址
7. 将保留下来的分支 push 到远程,完成前端代码抽离及分支保留。

Tips:
如果仓库原本就比较小,那么 第2步 直接执行 git clone ,不设置 depth , 之后就进入 4、5、6、7 步即可,第 3 步就不需要了。

三、实际操作:

0. 安装 git-filter-repo

使用 brew 安装 git-filter-repo,也可百度、google 搜索其他方法,能安装上就好。

# 安装 git-filter-repo
$ brew install git-filter-repo

# 查看 git-filter-repo 版本
$ git-filter-repo --version
10401e45d5f0

安装时如果遇到以下错误,可能是 .bash_profile 中设置了 HOMEBREW_BOTTLE_DOMAIN 阿里云镜像等

tar: Error opening archive: Failed to open '/Library/Caches/Homebrew/downloads/40d8398e4a3d8973f8ed6624552162a05390ef1653e15904bf6a3961bf084cc5--pcre2-10.37.catalina.bottle.tar.gz'

解决办法:

  1. 方法一: 去除 .bash_profile 中的 HOMEBREW_BOTTLE_DOMAIN 镜像
  2. 方法二: 在终端执行 export HOMEBREW_BOTTLE_DOMAIN='' 命令,临时去除镜像
  3. 去除镜像后,再执行 brew install git-filter-repo 进行安装

官方安装说明:github.com/newren/git-…

1. 创建新的 git 仓库 备用

在 git.projects.com 中,创建新的工程 projects-front-end, 创建成功后备用

2. 通过设置 depth 浅 clone projects-backend 仓库 ,保留近 100次 commit
(原因:仓库太大,完整拿不下来, 或完整拿下来后,push到新仓库失败).

$ git clone --depth 100 git@git.projects.com:root/projects-backend.git projects-front-end

正克隆到 'projects-front-end'...
remote: Enumerating objects: 77200, done.
...
正在更新文件: 100% (49129/49129), 完成.

# 进入 clone 下来的仓库中,后续操作,都在此目录下执行
$ cd projects-front-end 

刚 clone 下来的文件目录如下

$ ll // 等价于 ls -al
total 8
-rwxr-xr-x    1   staff  1770 Apr 16 14:29 .gitignore*
drwxr-xr-x    3   staff    96 Apr 16 14:29 backend/
drwxr-xr-x   44   staff  1408 Apr 16 14:29 projects/
drwxr-xr-x   14   staff   448 Apr 16 14:30 .../
drwxr-xr-x    3   staff    96 Apr 16 14:30 .../

3. 分别 fetch 要保留的分支(因为 浅clone 下来的仓库,只有 master 分支的代码,想要其他的分支,需单独做分支拉取操作)

如果第 2 步 clone 时,没有设置 --depth ,这一步可跳过

$ git remote set-branches origin remote_branch_name
$ git fetch --depth 100 origin remote_branch_name
$ git checkout remote_branch_name

这里想保留哪些分支,就把 remote_branch_name 换成哪些分支名称就可以。

4. 使用 --subdirectory-filter,把 public、dist 等前端文件和文件夹提取到第一级(根目录),去除 projects 这层目录

如果想要抽离的文件 和 文件夹,原本就在根目录下,这一步可跳过

$ git filter-repo --subdirectory-filter projects/ --force
Parsed 12300 commits
New history written in 2153.92 seconds; now repacking/cleaning...
Repacking your repo and cleaning out old unneeded objects
HEAD 现在位于 55bf0f955 推送兼容
...
总共 182125(差异 110168),复用 172692(差异 106866),包复用 0
Completely finished after 2821.50 seconds.

# 查看结果 
$ ll 

├── ...
├── public
├── dist
├── package.json
├── package-lock.json
├── README.md
├── ...
├── ...
└── ...

5. 使用 filter-repo --path 拿取我们想要的文件和文件夹

$ git filter-repo --path public --path dist --path package.json --path package-lock.json --path README.md --force

# 查看结果 
$ ll

├── public
├── dist
├── package.json
├── package-lock.json
└── README.md

6. 将 remote origin 更改为 新仓库的地址

$ git remote add origin git@git.projects.com:root/project-front-end.git

# 查看设置
$ git remote -v
origin git@git.projects.com:root/project-front-end.git (fetch)
origin git@git.projects.com:root/project-front-end.git (push)

7.将保留下来的分支 push 到远程,完成前端代码抽离及分支保留。

# 全部 push 上去
$ git push --all 

# or

# 只 push 某些分支
$ git push origin one_branch_name 

# 如果失败,可以加 --force
$ git push --all --force

然后就可以访问远程仓库,查看提取的文件及分支了。

补充:如果采用只拿取部分历史记录的方式抽离分支(--depth n),后续分支之间第一次合并时会报错 refusing to merge unrelated histories. 解决方案为:

git merge 时添加 --allow-unrelated-histories 强制合并,参考命令如下:

$ git merge <对应的分支> --allow-unrelated-histories

参考文档:将子文件夹拆分成新仓库

参考文章:利用git-filter-repo无缝迁移git项目