108

首先,我知道这一点:您将如何为内部软件项目组织 Subversion 存储库? 接下来是实际问题:我的团队正在重组我们的存储库,我正在寻找有关如何组织它的提示。(在这种情况下为 SVN)。这就是我们想出的。我们有一个存储库、多个项目和多个 svn:externals 交叉引用

\commonTools /*tools used in all projects. Referenced in each project with svn:externals*/
   \NUnit.v2.4.8
   \NCover.v.1.5.8
   \<other similar tools>
\commonFiles /*settings strong name keys etc.*/
   \ReSharper.settings
   \VisualStudio.settings
\trash /*each member of the team has trash for samples, experiments etc*/
   \user1
   \user2
\projects
   \Solution1 /*Single actual project (Visual Studio Solution)*/
      \trunk
         \src
             \Project1 /*Each sub-project resulting in single .dll or .exe*/
             \Project2
         \lib
         \tools
         \tests
         \Solution1.sln
      \tags
      \branches
   \Solution2
      \trunk
         \src
             \Project3 /*Each sub-project resulting in single .dll or .exe*/
             \Project1 /*Project1 from Solution1 references with svn:externals*/
         \lib
         \tools
         \tests
         \Solution2.sln
      \tags
      \branches

明确词汇:解决方案意味着单个产品,项目是一个 Visual Studio 项目(导致单个 .dll 或单个 .exe)

这就是我们计划布局存储库的方式。主要问题是,我们有多个解决方案,但我们希望在解决方案之间共享项目。我们认为将这些共享项目转移到他们自己的解决方案中没有任何意义,因此我们决定使用 svn:externals 在解决方案之间共享项目。我们还希望将通用工具集和第 3 方库保存在存储库中的一个位置,并在每个解决方案中使用 svn:externals 引用它们。

你觉得这个布局怎么样?尤其是关于 svn:externals 的使用。这不是一个理想的解决方案,但考虑到所有利弊,这是我们能想到的最好的解决方案。你会怎么做?

4

8 回答 8

92

如果您遵循以下我的建议(多年来一直如此),您将能够:

-- 将每个项目放在源代码控制中的任何位置,只要您保留项目根目录中的结构即可

-- 在任何机器上的任何地方构建每个项目,以最小的风险和最少的准备

-- 完全独立地构建每个项目,只要您可以访问其二进制依赖项(本地“库”和“输出”目录)

-- 构建和使用任何项目组合,因为它们是独立的

-- 构建和使用单个项目的多个副本/版本,因为它们是独立的

-- 避免将您的源代码控制存储库与生成的文件或库混在一起

我推荐(这里是牛肉):

  1. 定义每个项目以生成单个主要可交付成果,例如 .DLL、.EXE 或 .JAR(Visual Studio 的默认设置)。

  2. 将每个项目构建为具有单个根的目录树。

  3. 在其根目录中为每个项目创建一个自动构建脚本,该脚本将从头开始构建它,不依赖于 IDE(但如果可行,请不要阻止它在 IDE 中构建)。

  4. 考虑 nAnt 用于 Windows 上的 .NET 项目,或基于您的操作系统、目标平台等的类似项目。

  5. 让每个项目构建脚本从单个本地共享“库”目录引用其外部(第 3 方)依赖项,每个这样的二进制文件都由 version: %DirLibraryRoot%\ComponentA-1.2.3.4.dll,完全标识%DirLibraryRoot%\ComponentB-5.6.7.8.dll

  6. 让每个项目构建脚本将主要可交付成果发布到单个本地共享“输出”目录:%DirOutputRoot%\ProjectA-9.10.11.12.dll, %DirOutputRoot%\ProjectB-13.14.15.16.exe.

  7. 让每个项目构建脚本通过“库”和“输出”目录中的可配置和完全版本化的绝对路径(见上文)引用其依赖项,并且没有其他地方。

  8. 永远不要让一个项目直接引用另一个项目或其任何内容——只允许引用“输出”目录中的主要可交付成果(见上文)。

  9. 通过可配置且完全版本化的绝对路径使每个项目构建脚本引用其所需的构建工具: %DirToolRoot%\ToolA\1.2.3.4, %DirToolRoot%\ToolB\5.6.7.8.

  10. 通过相对于项目根目录的绝对路径使每个项目构建脚本引用源内容: ${project.base.dir}/src, ${project.base.dir}/tst(语法因构建工具而异)。

  11. 始终需要项目构建脚本通过绝对的、可配置的路径(根植于可配置变量指定的目录)引用每个文件或目录:${project.base.dir}/some/dirs${env.Variable}/other/dir.

  12. 绝不允许项目构建脚本使用相对路径引用任何内容,例如.\some\dirs\hereor ..\some\more\dirs,始终使用绝对路径。

  13. 绝不允许项目构建脚本使用没有可配置根目录的绝对路径引用任何内容,例如C:\some\dirs\here\\server\share\more\stuff\there.

  14. 对于项目构建脚本引用的每个可配置根目录,定义将用于这些引用的环境变量。

  15. 尝试尽量减少配置每台机器时必须创建的环境变量的数量。

  16. 在每台机器上,创建一个 shell 脚本来定义必要的环境变量,这些环境变量特定于该机器(如果相关,也可能特定于该用户)。

  17. 不要将特定于机器的配置 shell 脚本放入源代码控制中;相反,对于每个项目,将项目根目录中的脚本副本作为模板提交。

  18. 要求每个项目构建脚本检查其每个环境变量,如果未定义它们,则使用有意义的消息中止。

  19. 要求每个项目构建脚本检查其每个依赖的构建工具可执行文件、外部库文件和依赖的项目可交付文件,如果这些文件不存在,则使用有意义的消息中止。

  20. 抵制将任何生成的文件提交到源代码控制的诱惑——没有项目可交付成果、没有生成的源代码、没有生成的文档等。

  21. 如果您使用 IDE,请生成您可以生成的任何项目控制文件,并且不要将它们提交到源代码控制(这包括 Visual Studio 项目文件)。

  22. 建立一个包含所有外部库和工具的官方副本的服务器,以便在开发人员工作站和构建机器上复制/安装。将其与您的源代码控制存储库一起备份。

  23. 建立一个没有任何开发工具的持续集成服务器(构建机器)。

  24. 考虑一个用于管理外部库和可交付成果的工具,例如 Ivy(与 Ant 一起使用)。

  25. 不要使用 Maven——它最初会让你开心,最终会让你哭泣。

请注意,这些都不是特定于 Subversion 的,而且大部分都适用于针对任何操作系统、硬件、平台、语言等的项目。我确实使用了一些操作系统和工具特定的语法,但仅用于说明- -我相信你会翻译成你选择的操作系统或工具。

关于 Visual Studio 解决方案的附加说明:不要将它们置于源代码管理中!使用这种方法,您根本不需要它们,或者您可以生成它们(就像 Visual Studio 项目文件一样)。但是,我发现最好将解决方案文件留给各个开发人员在他们认为合适的情况下创建/使用(但不要签入源代码管理)。我在我的工作站上保留了一个Rob.sln文件,我从中引用了我当前的项目。由于我的项目都是独立的,我可以随意添加/删除项目(这意味着没有基于项目的依赖项引用)。

请不要使用 Subversion 外部组件(或其他工具中的类似组件),它们是反模式,因此是不必要的。

当您实施持续集成时,或者甚至当您只想自动化发布过程时,请为其创建一个脚本。制作一个单一的 shell 脚本:获取项目名称(在存储库中列出)和标签名称的参数,在可配置的根目录中创建一个临时目录,检查给定项目名称和标签名称的源代码(通过构造在 Subversion 的情况下适当的 URL)到该临时目录,执行一个干净的构建,运行测试并打包交付物。这个 shell 脚本应该适用于任何项目,并且应该作为“构建工具”项目的一部分检查到源代码管理中。您的持续集成服务器可以使用此脚本作为构建项目的基础,或者它甚至可以提供它(但您仍然可能需要自己的)。

@VonC:您不想在构建脚本中断时一直使用“ant.jar”而不是“ant-abcdjar”,因为您在不知不觉中使用了不兼容的Ant版本运行它。这在 Ant 1.6.5 和 1.7.0 之间尤其常见。概括地说,您总是想知道正在使用的每个组件的特定版本,包括您的平台(Java ABCD)和您的构建工具(Ant EFGH)。否则,您最终会遇到一个错误,您的第一个大问题将是跟踪涉及的各种组件的版本。最好提前解决这个问题。

于 2008-11-20T01:02:37.713 回答
3

我相信使用 Subversion 的实用版本控制具有组织存储库所需的一切。

于 2008-10-21T18:21:30.937 回答
3

我们已经将我们的设置几乎完全匹配您发布的内容。我们使用一般形式:

\Project1
   \Development (for active dev - what you've called "Trunk", containing everything about a project)
   \Branches (For older, still-evolving supported branches of the code)
       \Version1
       \Version1.1
       \Version2
   \Documentation (For any accompanying documents that aren't version-specific

虽然我认为不像你的例子那么完整,但它对我们来说效果很好,让我们把事情分开。我喜欢每个用户都拥有一个“Thrash”文件夹的想法——目前,这些类型的项目最终不会出现在源代码控制中,我一直认为他们应该这样做。

于 2008-10-21T18:32:22.367 回答
1

为什么将所有内容都放在一个存储库中?为什么不为每个项目建立一个单独的存储库(我的意思是“解决方案”)?

好吧,至少我已经习惯了每个存储库一个项目的方法。您的存储库结构对我来说似乎过于复杂。

您打算将多少项目放入这个大型存储库中?2?3? 10?100?

当你取消一个项目的开发时你会怎么做?只需从存储库树中删除它,以便将来很难找到它。还是让它永远躺在那里?或者当您想将一个项目完全移动到另一台服务器时?

那么所有这些版本号的混乱呢?一个项目的版本号为 2、10、11,而另一个项目的版本号为 1、3、4、5、6、7、8、9、12...

也许我很愚蠢,但我喜欢每个存储库一个项目。

于 2008-10-21T18:57:23.127 回答
0

我有类似的布局,但我的树干、树枝、标签一直在顶部。所以:/trunk/main、/trunk/utils、/branches/release/等。

当我们想尝试其他版本控制系统时,这最终非常方便,因为许多翻译工具最适合基本的教科书 SVN 布局。

于 2008-10-21T18:33:19.463 回答
0

我认为提议的结构的主要缺点是共享项目只会使用它们添加到的第一个解决方案进行版本控制(除非 svn:externals 比我想象的更漂亮)。例如,当您为 Solution2 的第一个版本创建分支时,Project1 不会被分支,因为它位于 Solution1 中。如果您稍后需要从该分支构建(QFE 版本),它将使用最新版本的 Project1,而不是分支时的 Project1 版本。

出于这个原因,将共享项目放在一个或多个共享解决方案中(因此是结构中的顶级目录),然后在任何解决方案的每个版本中分支它们可能是有利的。

于 2008-10-21T18:55:01.713 回答
0

要添加到相对路径问题:

我不确定这是一个问题:
只需在名为“Solution1”的目录下检出 Solution1/trunk,Solution2 同上:“目录”的目标实际上代表分支,一旦导入工作区,就不再可见。因此,“Solution1”(实际上是“Solution1/trunk”)和“Solution2”(Solution2/trunk)之间可能存在相对路径。

于 2008-10-21T18:58:42.157 回答
0

RE:相对路径和共享文件问题-

似乎这是特定于 svn 的,但这不是问题。另一个人已经提到了单独的存储库,如果您有不同的项目引用任意其他项目,这可能是我能想到的最佳解决方案。如果您没有共享文件,那么 OP 解决方案(以及许多其他解决方案)将正常工作。

我们仍在解决这个问题,我现在必须解决 3 个不同的工作(不同的客户),因为我接管了设置不存在或糟糕的版本控制。

于 2008-10-21T19:11:49.807 回答