在Mercurial中组织具有共享依赖项的项目的好方法是什么?

Rob*_*t P 62 mercurial project-management subrepos

目前,我正在从遗留版本控制系统转移并将我的团队项目转移到mercurial.作为我正在移动的代码类型的一个例子,我有一个25+项目的Visual Studio解决方案,包含几个独立的应用程序区域,这些区域都依赖于公共代码.查看Stack Overflow,我发现的最接近的问题就是这个问题,但它只提到了版本控制.我正在寻找有关使用Mercurial管理这些依赖项的具体实现技术的进一步建议.

依赖关系的简化视图如下所示.(这仅用于说明和示例;实际依赖性明显更复杂,但性质相似.)

                     Common Lib 1
                    /      |      \
                ----       |       -----   
               /           |        \   \
            App 1    Common Lib 2    \ App 2
                       /   |   \      \
                -------    |    ------ |
               /           |          \|
             App 3       App 4      App 5
Run Code Online (Sandbox Code Playgroud)

Common Lib模块将是共享代码 - 这将是一个DLL或SO或一些其他库,可以在编译和运行时同时在所有应用程序之间使用.否则,应用程序将能够彼此独立地运行.

设置我的mercurial存储库有几个目标:

  • 为每个重要的应用程序或组件组提供自己的存储库
  • 使每个存储库自包含.
  • 使项目的总和自包含.
  • 可以轻松地立即构建整个代码库.(最终,所有这些程序和库最终都在一个安装程序中.)
  • 把事情简单化.

另一点是我有一个服务器设置,我为每个项目都有单独的存储库.

我看到了几种铺设这些项目的方法.

1.创建一个包含所有内容的"Shell"存储库.

这将使用基于url的子版本(例如,在.hgsub中,我会做类似的事情.)做出来,它将如下所示:App1 = https://my.server/repo/app1

+---------------------------+
| Main Repository           |
| | +---------------------+ |
| +-| Build               | |
| | +---------------------+ |
| | +---------------------+ |
| +-| Common Lib 1        | |
| | +---------------------+ |
| | +---------------------+ |
| +-| Common Lib 2        | |
| | +---------------------+ |
| | +---------------------+ |
| +-| App 1               | |
| | +---------------------+ |
| | +---------------------+ |
| +-| App 2               | |
| | +---------------------+ |
| | +---------------------+ |
| +-| App 3               | |
| | +---------------------+ |
| | +---------------------+ |
| +-| App 4               | |
| | +---------------------+ |
| | +---------------------+ |
| +-| App 5               | |
|   +---------------------+ |
+---------------------------+
Run Code Online (Sandbox Code Playgroud)

shell存储库中的每个主文件夹都包含一个subrepo,每个项目区域都有一个子文件夹.依赖关系是相对的:例如,由于App 4需要Common Lib 2,它只需使用相对路径来引用该公共库.

这种方法的优点:

  • 每个库只下拉一次.
  • Mercurial的子计划将确保在所有项目中自动使用相同版本的库,因为项目中只存在该子项目的一个版本.
  • 找到每个资源很容易.

这种方法的缺点:

  • 我不能独立地在应用程序上工作.例如,如果我在App 2上工作,并且它需要更改公共库,那么所有其他应用程序将不得不立即进行这些更改.
  • 如果我自己拉一个App repo,如果我想构建它,我必须弄清楚(或知道)手工需要的其他依赖的repos.
  • 依赖关系没有很强的分离 - 在任何地方插入新功能都很诱人,因为很容易获得所有功能.

2.完全包含从属子目录.

在这种方法中,每个应用程序都有自己的存储库(如前所述),但这次也包含子存储库:一个用于自己的源,一个用于每个从属子存储库.然后,整个存储库将包含每个项目存储库,并知道如何构建整个解决方案.这将如下所示:

+-----------------------------------------------------------------------+
| Main Repository                                                       |
| +--------------------+ +--------------------+ +--------------------+  |
| | Build              | | Common Lib 1       | | Common Lib 2       |  |
| +--------------------+ | | +--------------+ | | | +--------------+ |  |
|                        | +-| Lib 1 Source | | | +-| Common Lib 1 | |  |
|                        |   +--------------+ | | | +--------------+ |  |
|                        |                    | | | +--------------+ |  |
|                        |                    | | +-| Lib 2 Source | |  |
|                        |                    | |   +--------------+ |  |
|                        +--------------------+ +--------------------+  |
| +--------------------+ +--------------------+ +---------------------+ |
| | App 1              | | App 2              | |  App 3              | |
| | | +--------------+ | | | +--------------+ | |  | +--------------+ | |
| | +-| Common Lib 1 | | | +-| Common Lib 1 | | |  +-| Common Lib 2 | | |
| | | +--------------+ | | | +--------------+ | |  | +--------------+ | |
| | | +--------------+ | | | +--------------+ | |  | +--------------+ | |
| | +-| App 1 Source | | | +-| App 2 Source | | |  +-| App 3 Source | | |
| |   +--------------+ | |   +--------------+ | |    +--------------+ | |
| +--------------------+ +--------------------+ +---------------------+ |
| +--------------------+ +--------------------+                         |
| | App 4              | | App 5              |                         |
| | | +--------------+ | | | +--------------+ |                         |
| | +-| Common Lib 2 | | | +-| Common Lib 1 | |                         |
| | | +--------------+ | | | +--------------+ |                         |
| | | +--------------+ | | | +--------------+ |                         |
| | +-| App 4 Source | | | +-| Common Lib 2 | |                         |
| |   +--------------+ | | | +--------------+ |                         |
| +--------------------+ + | +--------------+ |                         |
|                        | +-| App 5 Source | |                         |
|                        |   +--------------+ |                         |
|                        +--------------------+                         |
+-----------------------------------------------------------------------+
Run Code Online (Sandbox Code Playgroud)

优点:

  • 每个应用程序都可以自己构建,彼此独立.
  • 可以按应用程序跟踪库的依赖版本,而不是全局跟踪.它需要一个明确的行为,即在项目中插入一个subrepo来添加一个新的依赖项.

缺点:

  • 在进行最终构建时,每个应用程序可能正在使用不同版本的共享库.(可能需要编写工具来同步常见的lib子目录.Eww.)
  • 如果我想构建整个源代码,我最终会多次下载共享库.在Common Lib 1的情况下,我将不得不拉八(!)次.

3.根本不包含依赖项作为子目录 - 将它们作为构建的一部分引入.

这种方法看起来很像方法1,除了公共库只作为构建的一部分被提取.每个应用程序都会知道它需要什么样的存储库,并将它们放在公共位置.

优点:

  • 每个应用程序都可以自行构建.
  • 公共库只需要拉一次.

缺点:

  • 我们必须跟踪每个应用程序当前使用的库的版本.这复制了subrepo功能.
  • 我们必须构建一个基础架构来支持这个,这意味着更多的东西进入构建脚本.啊.

还有什么?

还有另一种处理方式吗?更好的方法?你尝试过什么方法并取得了成功,你尝试过哪些方式但却讨厌?我目前倾向于1,但缺乏应用程序独立性,应该能够,真的困扰我.有没有办法在没有大量重复代码拉动和依赖性维护的噩梦的情况下获得方法2的良好分离,而不必编写脚本来处理它(如选项3中)?

bar*_*jak 6

在我看来,依赖关系管理是项目组织的一个重要方面.您根据Mercurial的subrepos功能详细介绍了各种解决方案,并且我同意您提供的所有优点/缺点.

我认为SCM不太适合依赖管理.我更喜欢有一个专门的工具(这将是你的解决方案n°3).

我目前的项目是Java.它是用Apache Ant构建的,我首先将Apache Ivy设置为依赖管理工具.最后,设置包括共享目录中的一些常春藤配置文件,以及列出项目每个模块的依赖项的一个XML文件.Ivy可以被Ant目标调用,所以我在每个模块中添加了两个新动作:"解析依赖关系"和"部署构建的工件".部署会在共享目录中添加buid(称为工件)的结果.依赖关系解析意味着可传递地解析模块的依赖关系,并将已解析的工件复制到模块源的"lib"文件夹中.

此解决方案适用于C++项目,因为Ivy并非特定于管理Java依赖项:工件可以是任何东西.在C++中,模块生成的工件将是:

  1. 运行时的so/dll
  2. 编译时的头文件.

这不是一个完美的解决方案:Ivy不容易设置,您仍然必须告诉构建脚本使用哪些依赖项,并且您无法直接访问依赖项的源以进行调试.但是你最终会得到独立的SCM存储库.

在我们的项目中,我们然后将表单Ant + Ivy切换到Apache Maven,它负责构建和依赖管理.工件部署在Apache Archiva而不是共享文件夹中.这是一个巨大的改进,但它只适用于Java项目.