我已经使用Git或颠覆来完成重构/重新创建现有代码存储库的任务。在这种特殊情况下,存储库历史并不一定重要。经过分析,我发现在确定一个好的布局时有些问题。我读过很多博客和帖子,但我仍然不确定什么是最好的布局。
现有的存储库包含一组包含文件、一组相互依赖的库,它们中的许多依赖于包含文件集。此外,还有两个应用程序项目依赖于库集。此外,还有一组脚本使用其中一个应用程序和一些附加的配置信息。我画了一张图表来说明情况:

    +---------->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/
  ...

每次创建一个新版本时,我只需将整个存储库复制到标记中的一个新子目录。
此解决方案的问题在于库本身没有单独的标记目录,而且我只希望有一个包含标记组件的版本,并且此解决方案不显示哪些组件在版本中具有哪些标记版本。我已经考虑过使用单独的存储库,然后创建一个主存储库,其中有一个releases子目录,我在其中使用“svn:externals”和一个特定的tags子目录链接所有必需的组件,但是不同的库和include文件相互依赖,我看不到如何将代码划分为单独的实体。
有什么想法吗?
=2011年1月28日继续提问===============
好的,我画了一张我如何规划新布局的图。目标是将
使用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:外部属性也是。这会引起问题吗,还是需要这种行为?
我可以为所有外部对象指定一个显式的修订,但是标记无论如何都不应该更改,所以目录不够吗?
将存储库拆分为开发存储库和使用svn:external的存储库是否是更好的解决方案?参见问题中的“用例”答案What's the benefits of "svn:externals"?

最佳答案

我建议把分类法翻个底朝外。在Subversion中,我建议使用这样的分类法:

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中的头可能依赖于librarya项目的稳定版本)。
这种结构也适用于大多数构建工具,如maven、rake、buildr、ant等。
您提供的分类法看起来是应用程序部署版本的良好结构,但不是版本控制的良好结构。根据经验,我认为如果你使用我建议的版本控制结构,然后在打包/部署/发布应用程序时使用生成脚本(或生成工具)创建列出的结构,你会更好。
更新:要详细说明工作周期可能如何进行:
例如,假设我们已经完成了application1的bug修复(我们称这个版本为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版。您需要增强应用程序1。所以,在application1/trunk中进行更改。然后更新application1/trunk/readme.txt,说明您现在依赖于libraryd v1.3.0(同样,application1v1.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中,标记都是在给定时间点对一组文件的引用。这意味着标记不会占用太多空间,所以我说标记要早并且经常;-)

07-24 13:22