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 <directory> 重写子路径的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 <directory: ugc/publish> -- --all
3. mkdir <directory: ugc/publish >
4. mv * <directory: ugc/publish >
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 <repo B directory>
2. git remote add <from_repo_A> < repo_A_directory>
3. git pull <from_repo_A> master --allow-unrelated-histories 
4. git remote rm <from_repo_A>

迁移完成后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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 141,558评论 1 298
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 60,739评论 1 254
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 93,327评论 0 211
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 40,752评论 0 174
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 48,452评论 1 252
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 38,617评论 1 171
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 30,286评论 2 267
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,083评论 0 165
  • 想象着我的养父在大火中拼命挣扎,窒息,最后皮肤化为焦炭。我心中就已经是抑制不住地欢快,这就叫做以其人之道,还治其人...
    爱写小说的胖达阅读 28,839评论 6 227
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 32,413评论 0 213
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,186评论 2 213
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 30,506评论 1 223
  • 白月光回国,霸总把我这个替身辞退。还一脸阴沉的警告我。[不要出现在思思面前, 不然我有一百种方法让你生不如死。]我...
    爱写小说的胖达阅读 24,171评论 0 31
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,049评论 2 213
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 31,417评论 3 202
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,588评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 25,942评论 0 163
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 33,392评论 2 228
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 33,499评论 2 229

推荐阅读更多精彩内容