7

我知道关于同一件事有很多问题,但我仍然需要更多信息。我正在研究将我们的 SVN 存储库迁移到 git 的可能性,并试图了解哪种方法(单体主干、子模块、子树等)最适合我们的存储库。

以下是有关我们的项目和 SVN 存储库的一些信息:

  • 项目是java web 应用程序打包是war。
  • 它是模块化应用程序。每个模块由单独的团队开发,然后打包为 jar。
  • 战争取决于这个罐子。

基本上我们的结构看起来像:

repo
|-application(war)
|-module1 (for example, ui stuff)
|--module1Submodule1
|--module1Submodule2
|-module2 (for example, database access stuff)
|-...

每个模块都有自己的标签和分支。

我的本地机器上包含所有分支、标签等的 svn repo 的大小是:

  • 超过 250 万个文件
  • 超过 20Gb 空间
  • 有 311615 次修订
  • 文件大多是源代码,没有大的二进制对象

典型用例:

  • 整个团队有 200 多名开发人员和 QA
  • 不同的团队致力于他们的模块/子模块。(这可能是整体 git repo 的问题,因为 git 需要在推送之前拉出所有更改,svn 仅警告过时的更改)
  • 分支模块
  • 分行申请

未来的用例:

  • 格里特
  • 开发人员提交,提交审核,针对提交运行测试,如果绿色,则提交被批准合并到“主”分支

问题是:

  1. 我们可以认为这样的 repo 对 git 来说很大吗(我的意思是有很多帖子指出 git 对于大型 repos 的扩展性很差,但什么是“大”?)
  2. 每种方法的优缺点是什么:
    • Monolith repo(只是 git 作为 svn,反模式?)
    • 子模块
    • 子树(我对模块中的每个更改都需要在子树存储库中提交然后将更改拉到聚合子树存储库吗?)
    • 每个模块的单独存储库
    • 任何其他..
  3. 可以为每个人保留来自 SVN 的历史记录吗?
  4. 我需要尽可能多的链接(我没有找到任何“大型回购缓慢”的官方链接)

先感谢您!

4

3 回答 3

7

历史

使用 git svn 可以为所有提到的方法保留历史记录:http: //git-scm.com/book/en/Git-and-Other-Systems-Migrating-to-Git 甚至可以切换回以前的提交。

但是,有人建议不要保留历史记录,只需将 svn 存储库冻结大约 6 个月,而所有历史记录都会在 git 存储库中更改。我不同意这样的建议,因为历史对我们的项目至关重要。我敢打赌没有人接受这样的解决方案。

巨型树干方法

  • 您必须克隆整个大树,即使您只计划处理一个子目录(主要用例)
  • 一些 git 命令会很慢(例如:git status,因为它需要检查整个树)
  • 即使您将 jenkins 调整为仅针对 repo 的特定部分触发构建(这可以使用 jenkins git 插件的“include”属性来完成)。仍然需要拉取所有 repo 来执行构建。这几乎不会影响所有的工作,因为即使是构建小模块,“干净”的结账也会花费很多时间。

关注点:整个团队有 200 多名开发人员和质量保证人员,我怀疑最终推动更改会很困难。

  • 只有在 gerrit 审核通过并通过测试后,才会将更改推送到 master 分支,因此我们不会有持续的 pull-push-fail-pull-push 流程
  • 但是,如果主分支在提交被推送到 gerrit 后发生更改,则 gerrit 可能会拒绝合并,它将需要单击“rebase”按钮并重新运行测试。
  • Linux 内核有单体 repo,因为 c/c++ 没有像 java 那样的依赖管理:构建一个内核 tar 像战争与 jar 依赖不是这种情况。

测验

使用这种方法迁移的步骤、成本和总成本是什么?

  • git svn clone SVN_URL REPO_NAME
  • 詹金斯的东西

它如何支持代码门控?从 VCS / 工具的角度来看,需要进行哪些更改?在这里假设完整的 CI 运行需要 15 分钟。

  • Jenkins 应该在 scm 触发器中包含“包含”过滤器,以过滤项目特定部分的更改。并不难,但仍然需要一些努力来设置和验证它们。在“构建前擦除工作空间”构建的情况下,应该一直克隆整个 repo。它可以增加从提交到“测试批准”的总体时间,因为结帐会很慢。

什么是高效的开发人员工作流程?

  • 开发人员使用本地/远程功能分支
  • 将更改推送到 gerrit
  • Gerrit 根据测试验证更改
  • 更改合并到主分支

子模块

此处解释了大多数警告http://git-scm.com/book/en/Git-Tools-Submodules和此处http://codingkilledthecat.wordpress.com/2012/04/28/why-your-company-shouldnt-use -git子模块/

主要问题是您将不得不提交两次

  • 子模块本身
  • 聚合回购 - 更新子模块没有意义。如果依赖项是通过工件存储库管理的,为什么还需要聚合存储库?

实际上,子模块是为存在可以在不同项目中重用的库的情况下创建的,但是您希望依赖库的特定标签以能够在将来更新参考。但是,我们不会标记每个提交(仅在每次提交后发布),并且在战争中更改依赖项版本(到已发布的版本)将比维护子模块方法更容易。Java 依赖管理使事情变得更简单。

不建议指向子模块头部,会导致子模块出现问题,因此这种方法对于快照来说是死路一条。再一次,我们不需要它,因为 java 依赖管理将为我们做所有事情。

测验 使用这种方法迁移的步骤、成本和总成本是什么?

  • git svn clone 每个模块的 SVN_URL REPO_NAME
  • 创建聚合 git repo
  • 将模块存储库作为子模块添加到聚合存储库

它如何支持代码门控?从 VCS / 工具的角度来看,需要进行哪些更改?在这里假设完整的 CI 运行需要 15 分钟。

  • Gerrit 支持合并和提交子模块,所以应该没问题。
  • Jenkins 的东西 - 触发子模块更改和聚合 repo 更改(啊!在两个地方没有意义!)

什么是高效的开发人员工作流程?(省略Gerrit进程)

  • 开发人员提交到子模块
  • 标记他的提交
  • 开发人员进入聚合回购
  • cd 进入子模块,结帐标签
  • 提交具有更改的子模块哈希的聚合 repo

或者

  • 开发者更改子模块
  • 将更改推送到子模块以不丢失更改
  • 提交具有更改的子模块哈希的聚合 repo

如您所见,开发人员的工作流程很麻烦(需要始终更新两个位置)并且不适合我们的需求。

子树

主要问题是您必须提交两次树合并子目录将更改推送到原始仓库

子树是子模块的更好替代方案,它更健壮,并且将子模块的源代码合并到聚合 repo 而不是仅仅引用它。维护这样的聚合存储库使事情变得更简单,但是子树的问题与子模块的问题相同,进行双重提交是完全没用的。您不必被迫提交对原始模块 repo 的更改,并且可以通过聚合 repo 提交它,这可能导致 repos 之间的不一致......

这些差异在这里得到了很好的解释:http: //blogs.atlassian.com/2013/05/alternatives-to-git-submodule-git-subtree/

测验 使用这种方法迁移的步骤、成本和总成本是什么?

  • git svn clone 每个模块的 SVN_URL REPO_NAME
  • 创建聚合存储库
  • 对每个模块执行子树合并

它如何支持代码门控?从 VCS / 工具的角度来看,需要进行哪些更改?在这里假设完整的 CI 运行需要 15 分钟。

  • 看起来 Gerrit 支持子树合并不是很好(https://www.google.com/#q=Gerrit+subtrees
  • 但我们不能确定,直到尝试
  • 詹金斯的东西。触发子树 repo 和聚合 repo 更改(啊!在两个地方没有意义!)

什么是高效的开发人员工作流程?(省略Gerrit进程)

  • 开发人员更改子树中的某些内容(在聚合存储库中)
  • 开发人员提交聚合回购
  • 开发人员不会忘记将更改推送到原始仓库(没有意义!)
  • 开发人员不要忘记在一次提交中不要将子树更改与聚合 repo 更改混合

再次像子模块一样,有两个地方(repo)存在代码/更改是没有意义的。不适合我们的情况。

单独的回购

单独的 repos 看起来是一个最好的解决方案,并遵循原始的 git 意图。回购的粒度可能会有所不同。最细粒度的情况是每个 maven 发布组都有一个 repo,但是它可能导致太多的 repo。我们还需要考虑一个特定的 svn 提交影响多个模块或发布组的频率。如果我们看到,该提交通常会影响 3-4 个发布组,那么这些组应该形成一个 repo。

我也相信至少将 api 模块与实现模块分开是值得的。

测验 使用这种方法迁移的步骤、成本和总成本是什么?

  • git svn clone SVN_URL REPO_NAME 为每个或多或少的细粒度模块数

它如何支持代码门控?从 VCS / 工具的角度来看,需要进行哪些更改?在这里假设完整的 CI 运行需要 15 分钟。

  • Jenkins 分别为每个 repo 触发。没有“包含”过滤器。只需签出、构建、部署。

什么是高效的开发人员工作流程?

  • 开发人员为每个 repo 使用本地/远程功能分支
  • 将更改推送到 gerrit
  • Gerrit 根据测试验证更改
  • 更改合并到主分支
于 2013-11-19T07:50:20.250 回答
1

我将给你一个小答案。这很简单,可能还有很多不足之处,但它也可能有所帮助。

  1. 忘记历史。你什么时候需要它?你总是有旧的 svn 供参考,几个月后对它的需求就会减少。这并不总是实用的,但请仔细考虑您对旧代码的真正需求。

  2. 广泛使用分支。

  3. 为各种模块使用不同的 git 存储库。

  4. 在决定在 git 中做什么时忘记 svn 模型。

顺便说一句,如果您确实想要历史-$ git svn clone http://svn/repo/here/trunk

于 2013-11-13T23:03:44.593 回答
1

我也不认为你应该将历史从 SVN 转移到 Git。如果您确实需要保存历史记录,请将旧的 SVN 存储库保持在只读模式。IME、SVN 与 Git 的不同之处在于,转换历史实际上会产生误导性的历史。

为每个具有独立构建过程的事物使用单独的 git 存储库。这可能是在模块级别,或者或多或少的细粒度。然后,如果您真的需要它,您可以将这些存储库与仅具有目录结构和子模块的“超级”存储库“缝合”在一起。

使用钩子和配置来防止强制推送到 master 和任何其他共享分支。它应该很少需要完成,并且只是作为某些恢复过程的一部分,所以它应该由管理员而不是开发人员完成。但是,提供一个已知的分支“命名空间”,开发人员可以使用它与他们自己或其他人共享提交和分支,并让这些分支很容易被强制推送。

鼓励开发人员进行大量私有分支,但有一个清晰的、可管理的创建(分支)和删除(合并)共享分支的过程。Merge vs. rebase vs. rebase --squash 是一个悬而未决的问题,用于将提交从开发人员分支转移到共享分支,但作为一个团队做出决定并且所有人都使用一种风格。(我更喜欢合并,但其他人也可以接受。)尽快实施 Gerrit 或类似的东西,以便在代码显示在共享分支之前对其进行审查,并且将提交从开发人员转移到共享分支是自动化的(将策略转变为流程)。

高温高压

于 2013-11-15T20:08:06.857 回答