如何构建包含多个库和应用程序的存储库

时间:2011-01-25 16:38:09

标签: svn git version-control repository

我已经赢得了使用Git或Subversion重构/重新创建现有代码库的任务。在这种特殊情况下,存储库历史记录不一定重要。在分析了这种情况之后,我发现了一些确定好布局的问题。我已经阅读了很多博客和主题,但我仍然不确定什么是最好的布局。

现有存储库包含一组包含文件,一组部分依赖于彼此的库,其中许多依赖于包含文件集。此外,还有两个依赖于库集的应用程序项目。此外,还有一组脚本可以使用其中一个应用程序和一些其他配置信息。我画了一个图表来澄清情况:

    +---------->include files
    |                ^
    |                |
library A -----> library B <----- library C <----- library D
  ^  ^                                |                ^
  |  |                                |                |
  |  +--------------------------------+                |
  |                                                    |
application 1        application 2 --------------------+
  ^
  |
script -----> configuration information

目标是建立一个布局,其中每个组件可以尽可能独立地开发,并且有一个发布(对于外部客户),其中包含定义标签版本的所有组件集,以便可以去及时回来并为特定版本构建软件。

我想出了以下结构:

trunk/
  include/
  lib/
    library A/
    library B/
    library C/
    library D/
  app/
    application 1/
    application 2/
tags/
  1.0/
    include/
    lib/
      library A/
      library B/
      library C/
      library D/
    app/
      application 1/
      application 2/
  1.1/
    include/
    lib/
      library A/
      library B/
      library C/
      library D/
    app/
      application 1/
      application 2/
  ...

每次创建新版本时,我只需将整个存储库复制到标签中的新子目录。

这个解决方案的问题在于库本身没有单独的标记目录,而且我只想要一个由标记组件组成的版本,并且此解决方案不会显示哪些组件具有哪个标记版本发布。我考虑使用单独的存储库,然后创建一个主存储库,其中包含一个发布子目录,其中我使用`svn:externals'和特定的tags子目录链接所有必需的组件,但不同的库和包含文件相互依赖,我不喜欢看看如何将代码划分为单独的实体。

有什么想法吗?

===============问题在28-1-2011继续===============

好的,我已经绘制了一个关于如何规划新布局的图表。目标是链接 svn:externals方法中的各种依赖项的标记 存储库,例如我在中设置svn:externals trunk / projects / lib / library2 /依赖于^ / tags / projects / include / std / 1.3。

trunk/
  projects/
    include/
      std/
    lib/
      library1/
        dependencies/
          std/ --> tags/projects/include/std/1.2
      library2/
        dependencies/
          std/ --> tags/projects/include/std/1.2
          library1/ --> tags/projects/lib/library1/1.4.3
      library3/
        dependencies/
          std/ --> tags/projects/include/std/1.3
          library1/ --> tags/projects/lib/library1/1.4
    app/
      application1/
        dependencies/
          library3/ --> tags/projects/lib/library3/1.1
      application2/
        dependencies/
          library1/ --> tags/projects/lib/library1/2.1
      application3/
        dependencies/
          std/ --> tags/projects/include/std/1.2
          library2/ --> tags/projects/lib/library2/1.5.2
    config/
      configuration1/
        dependencies/
          application1/ --> tags/projects/app/application1/2.3
      configuration2/
        dependencies/
          application1/ --> tags/projects/app/application1/1.6
      configuration2/
        dependencies/
          application2/ --> tags/projects/app/application1/1.6
tags/
  projects/
    include/
      std/
        1.2/
        1.3/
    lib/
      library1/
        1.4.3/
        1.4/
        2.1/
      library2/
        1.5.2/
      library3/
        1.1/
    app/
      application1/
        1.6/
        2.3/
branches/
  ...

剩下的问题:

  • 这个设计是否可行,或者您认为有什么主要缺点吗?
  • 如果我将库复制到标签目录会怎样?这会复制 svn:externals属性。这会导致问题,还是需要这种行为?
  • 我可以为所有外部指定一个明确的修订版,但标签不应该改变,所以目录不够吗?
  • 将存储库拆分为开发存储库和使用svn:external的存储库是否是更好的解决方案?请参阅问题What's the benefits of "svn:externals"?中的答案“用例”。

2 个答案:

答案 0 :(得分:6)

我建议将分类法彻底改变。在颠覆中,我建议像这样的分类:

companyname/
  include/
    trunk/
    tags/
    branches/
  libraryA/
    trunk/
    tags/
    branches/
  libraryB/
    trunk/
    tags/
    branches/
  libraryC
    trunk/
    tags/
    branches/
  libraryD/
    trunk/
    tags/
    branches/
  application1
    trunk/
    tags/
    branches/
  application2
    trunk/
    tags/
    branches/

在git中,我建议你为include,libraryA,libraryB,application1等创建一个单独的git repo ...

此结构允许您在不同部分之间创建任何类型的依赖关系(例如,application1中的分支可能依赖于libraryA项目的不稳定版本,而application1中的HEAD可能依赖于稳定版本的libraryA项目)。

此结构也适用于大多数构建工具,如maven,rake,buildr,ant等。

您提供的分类法看起来对于部署的应用程序版本来说是一个很好的结构,但不是一个很好的版本控制结构。根据经验,如果您使用类似我建议的版本控制结构,我认为您会更好,然后使用构建脚本(或构建工具)来创建您在打包/部署/时所列出的结构运送你的应用程序。

更新:详细说明工作周期可能会如何:

因此,举例来说,我们说我们已经完成了对Application1的错误修复(让我们称之为1.0.0版本)。最新和最大的更改将检入application1 / trunk。此版本的Application1依赖于libraryD v0.5.0。您更新application1 / trunk / README.txt,并注意此版本依赖于libraryD v0.5.0。或许,更重要的是,application / trunk中的构建脚本知道如何签出libraryD / tags / 0.5.0。接下来,在application1 / tags / 1.0.0中创建一个标签(它只是当前状态下的trunk的副本)。

现在,让我们说一周过去了,另一位开发人员将libraryD更新到版本1.3.0。您需要增强application1。因此,在application1 / trunk中进行更改。然后更新application1 / trunk / README.txt以表示您现在依赖于libraryD v1.3.0(同样,application1 v1.3.0的新构建脚本将检出libraryD / tags / 1.3.0)。将application1 / trunk复制到application1 / tags / 1.1.0。

现在,如果需要,您可以随时恢复到application1 / tags / 1.0.0(并确信它将从libraryD / tags / 0.5.0中提取代码.application / tags / 1.1.0将使用libraryD版本1.3.0 。

在subversion和git中,标记是指在给定时间点返回一组文件的引用。这意味着标签不会占用太多空间,因此我会提前和经常说标签; - )

答案 1 :(得分:1)

一个重要的问题是,所有图书馆和应用程序是否紧密耦合到单个产品中,或者它们是否可以彼此独立生活。 IMO将它们放入单个git存储库只是有意义的,如果所有的lib和应用程序都是一个单独的产品,因为除了svn之外,你不能只检查一个git树的一部分。当lib和应用程序是独立的时,您可以为每个lib / app创建一个repo,并通过子模块将它们粘合在一起。

Git子模块类似于svn extarnals,但它们始终引用目标的特定修订版,而不仅仅是普通的URL。因此,当您运行git checkout deadbeef时,您始终从创建commit deadbeef时获取子模块状态,无论哪个是引用的repo的头部。因此,git标记也会采用每个子模块的状态,这与带有非修订版固定网址的svn不同。