文档结构  
翻译进度:已翻译     翻译赏金:0 元 (?)    ¥ 我要打赏
参与翻译: solowolf (10)

2016年,我们将开发仓库从 Subversion 切换到Git。 由于我负责这项迁移工作的大部分,所以想分享一些这个过程的技术方面,主要集中在我们必须克服的最大的障碍:将多个 Subversion 仓库合并到一个Git仓库中,缩小仓库大小,处理 Subversion 外部环境以及由此引发的迁移过程中的变化。

合并多个Subversion仓库

我们的Subversion仓库

我们的产品 Teamscale 基于开源分析工具包 ConQAT ,在慕尼黑理工大学的 Competence Center Software Maintenance 开发,现在由我们维护。 Subversion 仓库中的第一个提交可以追溯到2005年1月,并涉及叫做 SVAT的 ConQAT 前身。 这个仓库仍然包含我们源代码的核心部分,并且在迁移时已经提交了 57.870 次。

第 1 段(可获 1.79 积分)

此外,我们在单独的 Subversion 仓库中对 ConQAT 和 Teamscale 做了专有分析。 自2009年以来,这个仓库共包含 26.155 次提交。客户特定的分析是在包含 12.405 次提交的第三个 Subversion 仓库中开发的。

在所有三个存储库中,我们有相同的分支策略:正在进行的开发在 trunk 分支上完成。 稳定的分支在同一时间从主干分叉,并从发布分支合并回主干定期修复。 我想我没有在这里告诉你什么新的东西,这是使用 Subversion 的标准方式。

第 2 段(可获 1.23 积分)

然而,我们在三个仓库上同步分支时遇到了产生额外开销的问题,这些开销是不需要的。 因此,我们决定迁移到Git时结合所有三个仓库的代码,同时保持原始分支结构并保留所有历史。

合并多个仓库

我们迁移的第一步是使用 git-svn 桥将 Subversion 仓库转换为 Git 仓库。 下一步是在已转换的仓库中构建一个新的 Git 仓库,将每个源仓库的分支合并到一个分支中。 每个仓库中分支上的提交将按照日期顺序挑选到最终目标仓库中的主干。 遗憾的是,没有集成实现的工具或脚本是这样做的,所以我们不得不编写自己的 Git Repository Zipper

第 3 段(可获 1.73 积分)

我发现一个zipper的比喻非常适合这个算法,你知道的就是夹克上的拉链。.下面的图简要说明了它如何工作。

仓库 A:

       A3----A5----A7 release-branch 
     /               \ 
A1---A2---A4---A6---A8 master 

仓库 B:

      B3----B5----B7 release-branch 
     /        \    \ 
B1---B2---B4---B6---B8 master 

合并后的仓库:

      B3-----A3----A5-----B5---------B7---A7 release-branch 
     /                     \          \   \ 
B1---A1---A2---B2---B4---A4---B6---A6---B8---A8 master 

此时要感谢来自 libgit2sharp 的令人赞叹的分支,它使C#可用于 Git API。

第 4 段(可获 0.65 积分)

减小仓库的大小

现在我们有一个Git仓库,包含我们的 Subversion 仓库中包括分支在内的所有东西。 它允许我们浏览文件的完整历史。 然而,仓库的大小超过6GB,这就导致克隆需要很长的时间,降低了常规Git操作的性能。 原因是 Subversion 存储库也用于存储文档和大型二进制数据(例如,版本,库,...)。 而Git不适合存储这种数据,因为它需要在初始克隆时下载整个仓库历史。

第 5 段(可获 1.19 积分)

我们执行以下操作来将整个存储库大小减少到 1GB 多:

  • 检测git仓库,使用 git filter-branch --index-filter从完整的Git历史记录中删除它们,去掉最大的(未使用的)文件。
  • 删除包含与我们开发不相关的代码或数据的整个文件夹。
  • 引入 Gradle 执行部分库依赖管理。

我们还在研究 Git LFS 存储大型二进制文件的用法,但在迁移时,对Eclipse中的Git LFS的支持仍然有限—这是我们的开发主要使用的IDE。 使用 git-replace 拆分历史记录被弃用,因为1 GB的最终结果对我们来说相当满意。 如果仓库持续增长,我们仍然有这些选项,但会采取主动手段来阻止这种情况,使用一个简单的服务器端提交钩来子拒绝大于5MB的文件的提交。

第 6 段(可获 1.81 积分)

摆脱 svn : 外部环境

迁移到Git的最大障碍是 Subversion 外部环境。 我们使用外部环境作为一种共享代码库之间的通用代码的手段,例如使用 Teamscale 服务器和 IDE客户端来防止冗余。 我们也使用外部环境来集中管理JAR。 Stackoverflow上的讨论关于使用Subversion外部环境依赖管理的反面模式我基本同意,所以正好利用迁移到Git的机会摆脱它们。

核心解决方案是引入一个 Gradle 任务,它必须在克隆Git仓库之后执行来引导开发环境:

第 7 段(可获 1.39 积分)
  • 对于使用外部环境来管理JAR,我们使用 Gradle 依赖代替。 引导任务从Maven Central下载JAR并将它们放在Java项目的lib文件夹中。
  • 对于使用外部环境来共享代码文件夹,引导任务将创建一个符号链接。 这样做有另外的好处,即在consuming project中编辑共享文件将编辑原始位置中的文件,并使修改立即在所有位置可见。 你可能会问,等等,Git 不支持直接使用符号链接吗?—是的,但这只适用于基于Unix的平台,我们有几个贡献者在Windows机器上工作。
第 8 段(可获 1.41 积分)

从长远来看,我们想要用真正的 Gradle 项目依赖来替换引导。 但这严重影响 ConQAT 类加载,我们的目标是不因为迁移而改变代码。 此外,引导程序现在也用于将Git仓库配置为一致的行结束样式,所有贡献者的 Git merge / pull 行为以及复制共享 Eclipse 项目设置。

Git只是开始…

实际上,Git的出现标志着开发过程进一步改变的开始:

  • 与迁移一起,我们改变了分支策略,仅在特征分支上开发,因此允许我们有一个稳定的总是准备好发布的主分支。
  • 功能分支需要更复杂的构建设置。 我们配置 Jenkins 作业来构建和测试每个推送的分支,并且将要迁移到 Jenkins2 管道工作流,以支持一个单片长期运行构建作业。
  • 此外,我们的代码受到影响:Git功能分支使用额外的工具,因为在单独的分支上进行更改,跟踪文件的审查状态显得多余。 在将分支合并到主数据库之前,我们可以简单地识别已更改的文件并进行审核。
第 9 段(可获 2.46 积分)

总结

迁移到Git(出乎意料)是平滑的:我已在一个shell脚本准备好所有迁移步骤,在一个星期五下午我们把 Subversion 仓库设置为只读模式,运行了几次。 整个迁移大约需要一天,所以在星期一早上Git仓库可供所有开发人员使用。 当然,在接下来的几个星期里,会出现如何在Git中做X事的问题—事实上,我在第一周做了相当多的帮助工作,如果你计划从集中版本控制系统像Subversion 或 Microsoft Team Foundation 迁移到 Git,我强烈建议考虑这一点。

如果你已经迁移了你的仓库,我乐意听取你的经历,或者如果你计划迁移,我也乐意回答你的问题。 你可以直接对这篇文章发表评论或通过邮件或Twitter联系我。

第 10 段(可获 1.89 积分)

文章评论