利用git-filter-repo无缝迁移git项目

背景

正所谓天下大事合久必分,分久必合。实际工作中的项目也类似,有的项目越来越大,或者有时候需要把没有前后端分离的项目代码拆分到两个仓库里,就涉及到对已有git项目的迁移操作。

如果只是简单的把代码拆开并不难,可以选择直接下载git项目源代码压缩包,拆分后push到新建的git仓库里就可以了。

但是现实世界往往没有那么简单,比如正在进行的开发分支还没有合并,或者我们想保留提交记录和分支关系。如此种种原因导致无法简单粗暴地把源代码扔到一个新建的git仓库里,还涉及对git记录进行必要的裁剪。

本文就介绍一个好用的工具来进行无损的git仓库迁移。

真实案例

在实际工作中,我们有一个项目,其项目目录结构很简单,是一个既含有前端代码,也含有服务端代码的仓库,如下所示:

server/
   foo.c
   bar.c
webapp/
   app.tsx
   components/
        index.tsx
zebra.jpg

我们希望将前端和服务端代码拆分成两个独立的git仓库,但是因为开发同学还有正在开发的功能分支没有合并,因此我们希望能够同时迁移代码和git提交历史以及分支。最终希望得到的结果是:

git repo A: ./server/

git repo B: ./webapp/

git-filter-branch命令

通过查看git文档,首先考虑使用git filter-branch命令来进行迁移。简单来说该命令可以用来操作目录树,同时修改历史提交记录。

在我还没来得及完全理解这个命令之前,就看到文档中有这样一段warning

git filter-branch has a plethora of pitfalls that can produce non-obvious manglings of the intended history rewrite (and can leave you with little time to investigate such problems since it has such abysmal performance). These safety and performance issues cannot be backward compatibly fixed and as such, its use is not recommended. Please use an alternative history filtering tool such as git filter-repo. If you still need to use git filter-branch, please carefully read SAFETY (and PERFORMANCE) to learn about the land mines of filter-branch, and then vigilantly avoid as many of the hazards listed there as reasonably possible.

这里提到了filter-branch命令由于有可能产生杂乱的提交历史,以及惨不忍睹的执行效率,所以最终推荐了一个第三方工具git filter-repo。接下来,就该今天的主角登场了。

git-filter-repo

github首页上,关于git-filter-repo有这样的描述

git-filter-repo可以胜任很多需要修改提交历史的场景,虽然它与git-filter-branch命令功能有些许重合,但摒弃了git-filter-branch那令人抓狂的执行效率。

在功能方面,git-filter-repo的人机交互设计让其面对简单的修改更加游刃有余,同时仍然可以像复杂的git-filter-branch命令一样完成庞杂的任务。

接下来我们考虑如何利用这个强大的工具来进行git项目的迁移。

首先,需要定义成功迁移的标准:

  • 删减server目录,仅保留webapp目录,并让webapp成为新的根目录

  • 推向新的git仓库

  • 确认在新的git仓库中仍然保留所有与webapp相关的分支

  • 确认迁移之后保留的分支历史记录应该与老仓库的历史记录一致

相关命令简介

查看git-filter-repo的文档可以看到有不少简单的示例,很幸运有一些例子正好可以解决我们的问题。

git-filter-repo的命令选项 (flag) 主要用来操作目录树,根据操作的目录树自动判断需要修改的git提交历史信息。

比如我们需要保留webapp目录,删除server目录,那么仅需执行:

git filter-repo --path 'webapp/'

这样仓库中的目录结构就会变为:

webapp/
   app.tsx
   components/
        index.tsx

可以看到,已经没有server文件夹了。

再比如如果我们希望删除.DS_Store文件以及其提交历史:

git filter-repo --invert-paths --path '.DS_Store'

当然通常我们需要删除根目录下所有.DS_Store文件及其历史,那么可以加上--use-base-name选项,表示匹配文件名,而不是匹配完整路径:

git filter-repo --invert-paths --path '.DS_Store' --use-base-name

其中 --path 选项后跟需要操作的路径或者文件名,--invert-paths选项字面意思是反向,因此该标记表示的是删除操作。

以上都是删除某些文件或者保留某些文件的操作,其目录结构仍然会保留原始仓库的结构,但我们需要的是仅保留webapp目录下的所有文件,并将其中的内容移动到根目录下。针对这种场景git-filter-repo提供了一个叫做--subdirectory-filter的选项,接下来就进行实际操作。

实际操作

⚠️ git-filter-repo需要在一个干净的,刚刚clone下来的仓库中进行操作,否则会提示操作并停止操作

接下来再看一下老的仓库目录结构

server/
   foo.c
   bar.c
webapp/
   app.tsx
   components/
        index.tsx
zebra.jpg

仅保留webapp目录下的内容,并让其成为新的根目录,执行如下命令:

git filter-repo --subdirectory-filter webapp/

执行结果的目录结构如下所示:

app.tsx
components/
    index.tsx

至此,目录结构已经如愿完成,为了确认迁移的历史记录也是完整的,执行

git branch # 观察是否所有需要保留的分支仍然存在
git log #观察分支的提交记录是否正确

如果确认本地仓库的迁移结果正确,再执行命令将当前本地仓库推向迁移的目的仓库即可:

git remote add <new-origin> git://somehost/some-project.git # 添加新的远程仓库
git push <new-origin> --all # 向新的仓库推送本地所有分支

至此我们的git项目迁移就完成了,不仅将代码迁移到新的仓库,也同时将提交历史带去了新的仓库。这样一来,对于开发同学来说迁移完全是无痛的,只是切换了新的git地址,而开发过程完全不会中断。

结语

在git项目需要迁移的场景中,日常工作中也许第一反应就是让开发同学们放下手中的工作,全部推向一个指定用于迁移的分支,然后以这个分支为准,下载源代码,再将其推送到新的仓库中。

这种操作方式虽然简单,但是对于开发同学来说,却会造成很多麻烦。

比如新仓库的代码不仅含有自己未开发完成的代码,也含有其他人未完成的代码,很有可能在迁移完成之后项目都跑不起来。

而除此以外,如果新仓库的代码有一个需要很快就上线的功能,或者紧急修复,面对一个百废待兴的新仓库,如何整理出一个可以上线的代码版本,又是一件非常头大的事情。

git-filter-branch可以帮助我们修改目录树和提交历史,但是执行效率和混乱的提交历史显得太过于繁琐。

git-filter-repo工具提供了强大的工具集,良好的用户使用界面,以及高效率的处理机制,在目前确实是处理仓库迁移的最优选择。

本文仅仅是讲解了git-filter-repo的一种应用场景,其官方文档上有更多的使用场景,可以根据更多的条件来过滤和操作文件和提交历史。

更多的精彩还请移步官方文档:

git filter-repo github

git filter-repo 文档

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

推荐阅读更多精彩内容