Git跨仓库迁移代码文件,并保留git历史记录

背景:

  由于架构优化、组件下沉、仓库调整等原因,在工作中时常需要将仓库A中的代码迁移到仓库B中,同时希望代码迁移后,能够保留其git历史记录。

目标:

  1. 解决A仓库全部迁移到B仓库,并保留git历史记录问题
  2. 解决A仓库中部分子目录文件迁移到B仓库,并保留git历史记录问题。
  3. 解决2中,可能丢失部分git记录的问题。

原理:

Git设置多个远程仓库,并分别pull下来

  git项目初始化后,默认情况下会有一个叫origin的远程仓库。我们也可以用 git remote add repo_A [repo_A的仓库地址] 来新增一个"远程"仓库,然后使用git pull,将多个仓库的代码拉下来并merge。这样便可以实现A仓库全部迁移到B仓库,并保留git历史记录。

  注意这里的[repo_A的仓库地址]也可以是本地仓库路径。比如我这里添加了两个repo,repo-A是位于本地路径的"远程"仓库。

image.png

Git filter-branch 重写历史

  我们已经知道了如何将A仓库全部迁移到B仓库,如果我们只需要将A仓库中的部分子目录文件迁移,就需要用到git重写历史的功能。git filter-branch 是git提供的重写历史的命令,我们可以用subdirectory-filter来实现重写git项目子路径的git历史记录。

--subdirectory-filter 重写子路径的git历史记录

   Only look at the history which touches the given subdirectory. The result will contain that directory (and only that) as its project root.

  subdirectory-filter可以从git项目中过滤出给定子目录的git历史记录,同时会把给定的子目录作为git项目的根目录。

  举例看下效果,原始repo_A的状态如下,有other和ugc俩个子目录,现在我们只需要迁移A仓库的ugc目录,因此需要先把ugc目录的git历史记录过滤出来。

image.png

  使用 git filter-branch --subdirectory-filter ./ugc -- --all重写./ugc路径的git历史。可以看到,仓库A只会保留ugc下的文件和git历史记录,其他的会被移除。再结合前面的原理,便可以实现A仓库中部分子目录文件迁移到B仓库,并保留git历史记录

image.png

  按上述的方案已经能够实现A仓库中部分子目录文件迁移到B仓库,但是某些情况下会丢失git记录。publishwtt是从publish模块抽离出来的,按上面的方案提取publishwtt路径的git历史记录,只能提取到新建publishwtt目录时间点之后的git记录,我们的情况是publishwtt刚从publish抽离不久,这样很多git历史会丢失,这是我们不愿意接受的。

   既然publishwtt从publish抽离来的,那么我们保留publishwtt和publish的父路径的git历史记录,就不会有git记录丢失的问题了。但是直接使用其父路径的历史记录,会把该目录的所有子目录的历史记录都保留下来,造成过多无用记录,我们还应该删除bytecert、profile等路径的git记录。可以用到index-filter来删除指定文件或文件夹,以及其git记录。

git filter-branch --force --index-filter 'git rm --cached -r --ignore-unmatch 文件路径' --prune-empty --tag-name-filter cat -- --all

   该命令会遍历所有commit记录,删除提交记录中包含指定文件的记录,如果删除后是空提交,把空提交记录也删除。subdirectory-filterindex-filter配合使用,可以较好的实现A仓库中部分子目录文件迁移到B仓库,并保留git历史记录。

实现过程:

  假设是要把repo A的 ugc/publish移动到repo B的 ugc/publish里面去。

image.png

第一步:在repo A里面准备要提交的目录。

  1. 可以先把remote端给remove掉,这样本地的修改一定不会影响到网络上的版本(可选项);
  2. 使用git filter-branch把需要的目录/文件筛选出来,放到对应目录里面去;
  3. 然后将此修改提交
1. git remote rm origin
2. git filter-branch --subdirectory-filter  -- --all
3. mkdir 
4. mv * 
5. git add .
6. git commit

  备注:一般的仓库执行会比较快,对于主业务仓库,比如头条ttmain有20多万次commit记录,执行时间会很长,命令跑起来后就可以先去干其他事了。另外,3-6行为修改文件路径,视情况决定是否需要执行。

第二步:将准备好的repo_A合并到repo_B中去。

  1. 切换到repo B的目录;
  2. 将本地的修改后的repo A添加为repo B的一个远程仓库,起名为from_repo_A
  3. 将这个名为from_repo_A的远程仓库 pull进来,并merge;
  4. 删除远程仓库 from_repo_A。
1. cd 
2. git remote add  < repo_A_directory>
3. git pull  master --allow-unrelated-histories 
4. git remote rm 

迁移完成后repo-B的sourcetree状态:

image.png

迁移子目录可能丢失git历史记录问题

  前面已经介绍过迁移子目录可能会丢失git历史记录的场景,这里直接介绍实现过程:

  上图已经是使用subdirectory-filter重写历史记录得到的git项目,再继续使用index-filter删除bytecert、profile等无关子目录的git历史记录。删除bytecert历史记录:

git filter-branch --force --index-filter 'git rm --cached -r --ignore-unmatch bytecert' --prune-empty --tag-name-filter cat -- --all

  该工程commit次数较多,每个删除任务执行时间较久,我们这里要删除的比较多,可以写一个.sh脚本,跑起来后,我们就可以去干其他事了。执行完后,将这个准备好的repo_A合并到repo_B中去即可。

git filter-branch --force --index-filter 'git rm --cached -r --ignore-unmatch profile' --prune-empty --tag-name-filter cat -- --all
git filter-branch --force --index-filter 'git rm --cached -r --ignore-unmatch bytecert' --prune-empty --tag-name-filter cat -- --all
...

总结:

  通过git的重写历史和支持设置多个远程仓库的能力,可以实现跨仓库迁移代码,并保留git历史记录。

参考文档:

  1. git-filter-branch
  2. removing-sensitive-data-from-a-repository

你可能感兴趣的:(Git跨仓库迁移代码文件,并保留git历史记录)