使用常见的嵌套子模块组织Git存储库


50

我是Git子模块的忠实粉丝。我希望能够跟踪依赖关系及其版本,以便您可以回滚到项目的先前版本,并具有相应版本的依赖关系,以安全,干净地进行构建。此外,将我们的库作为开放源代码项目发布更容易,因为库的历史记录与依赖于它们的应用程序的历史记录是分开的(并且不会公开)。

我正在为工作中的多个项目设置工作流程,我想知道如果我们采用这种方法有点极端,而不是拥有一个整体项目会怎么样。我很快意识到,在真正使用子模块时,可能存在蠕虫病毒。

假设有一对应用程序:studioplayer,以及依赖库coregraphnetwork,其中依赖关系如下:

  • core 是独立的
  • graph取决于core(的子模块./libs/core
  • network依赖于core(的子模块./libs/core
  • studio取决于graphnetwork(位于./libs/graph和的子模块./libs/network
  • player取决于graphnetwork(位于./libs/graph和的子模块./libs/network

假设我们正在使用CMake,并且每个项目都有单元测试和所有工作。每个项目(包括studioplayer)都必须能够独立编译以执行代码指标,单元测试等。

事情是,递归的git submodule fetch,然后您将获得以下目录结构:

studio/
studio/libs/                    (sub-module depth: 1)
studio/libs/graph/
studio/libs/graph/libs/         (sub-module depth: 2)
studio/libs/graph/libs/core/
studio/libs/network/
studio/libs/network/libs/       (sub-module depth: 2)
studio/libs/network/libs/core/

注意corestudio项目中克隆了两次。除了浪费磁盘空间之外,我还有一个构建系统问题,因为我要构建core两次,并且有可能获得的两个不同版本core

如何组织子模块,以便获得版本化的依赖关系和独立的构建,而无需获取公共嵌套子模块的多个副本?

可能的解决方案

如果库依赖关系只是一个建议(例如,以“已知可与版本X一起使用”或“仅正式支持版本X”的方式),并且潜在的依赖应用程序或库负责使用其喜欢的任何版本进行构建,则我可以想象以下情形:

  • 让构建系统知道graphnetwork告诉他们在哪里core(例如,通过编译器包含路径)。定义两个构建目标:“独立”和“依赖”,其中“独立”基于“依赖”,并添加包含路径以指向本地core子模块。
  • 引入额外的依赖性:studiocore。然后,studio建立core,设置包括它自己的拷贝路径core子模块,然后建立graphnetwork在“依赖”模式。

生成的文件夹结构如下所示:

studio/
studio/libs/                    (sub-module depth: 1)
studio/libs/core/
studio/libs/graph/
studio/libs/graph/libs/         (empty folder, sub-modules not fetched)
studio/libs/network/
studio/libs/network/libs/       (empty folder, sub-modules not fetched)

但是,这需要一些构建系统的技巧(我很确信这可以使用CMake来完成)以及版本更新方面的一些手动工作(更新graph可能还需要更新corenetwork获得core所有项目中的兼容版本) 。

有什么想法吗?


请注意,此问题并非特定于cmake:任何构建系统都存在,包括没有系统!(即,如果打算让超级项目仅添加库源;其中包括仅标头库)
MM

Answers:


5

我参加这个聚会已经很晚了,但是您的问题似乎仍然没有完整的答案,这在google上很受欢迎。

我在C ++ / CMake / Git / Submodules上有完全相同的问题,而在MATLAB / Git / Submodules上也有类似的问题,这由于未编译MATLAB而变得有些奇怪。我最近看了这段视频,似乎提出了“解决方案”。我不喜欢该解决方案,因为它本质上意味着丢弃子模块,但是确实消除了问题。就像@errordeveloper建议一样。每个项目都没有子模块。要构建项目,请创建一个超级项目来构建它,并将其作为其依赖项的同级对象。

因此,您的开发项目graph可能如下所示:

buildgraph/graph
buildgraph/core

然后您的Studio项目可能是:

buildstudio/studio
buildstudio/graph
buildstudio/network
buildstudio/core

超级项目只是一个主CMakeLists.txt模块和一堆子模块。但是,这些项目本身都没有任何子模块。

我看到的这种方法的唯一成本就是琐碎的“超级项目”的泛滥,这些“超级项目”仅用于构建您的真实项目。而且,如果有人掌握了您的一个项目,那么也很难找到不依赖超级项目的简单方法,即它的依赖性是什么。例如,这可能会使它在Github上显得非常丑陋。


1

我想当您将两者graphnetwork子模块集成到一起时studiocore在的历史记录中,给定的时间始终必须具有相同的版本studio。我会将studio/libs/core子模块简化为studio/libs/{graph,network}/libs

更新:

我使用您声明的依赖项创建了多个存储库:

./core      <--- (v2)
./graph
./graph/libs
./graph/libs/core  <--- (v2)
./graph/.gitmodules
./network
./network/libs
./network/libs/core  <--- (v1)
./network/.gitmodules
./studio
./studio/libs
./studio/libs/graph
./studio/libs/graph/libs
./studio/libs/graph/libs/core <--- (v1)
./studio/libs/graph/.gitmodules
./studio/libs/network
./studio/libs/network/libs
./studio/libs/network/libs/core  <--- (v1)
./studio/libs/network/.gitmodules
./studio/studio
./studio/.gitmodules

v1v2是的两个不同版本coregraph处理版本2,但network需要做一些工作,并停留在版本1中。在中studio,两个的本地嵌入式版本core都指向v1,以便具有有效的程序。现在,除了构建角度以外,所有子模块都可以正常工作。

我现在可以删除以下目录:

./studio/libs/network/libs/core

并将其替换为符号链接:

./studio/libs/network/libs/core@ -> ../../graph/libs/core/

我在本地提交此更改,并失去了具有两个单独的coreinside 版本的能力studio,但我只构建了core一次。准备升级到时v2,我可以执行以下操作:

 git submodule update # (--rebase ?)

...在studio / libs / network内部。


符号链接的想法确实引起了我的注意,但这不是一个解决方案。如果从graph/libs/core外部链接,则说明您不在使用子模块。如果您链接studio/libs/core到子模块自身的图书馆之一,那么你会选择哪一个,graph还是network?而且,当深度超过三层时会发生什么?最后,如果core可能会进行一系列修订。您是否想要链接到该版本的core任何一个graphnetwork正在使用,并不明显。
安德烈·卡隆(

“你选择哪一个?” :core是从原来的取一个子模块core库,更新后的版本,那就是同时兼容graphnetwork(你必须决定哪一个是好)。这些符号链接将添加到本地graphnetwork子模块中(未提取)。
coredump

1
您建议添加graphnetwork指向其自身存储库外部的符号链接(例如,studio项目中的其他位置)。他们如何知道何时使用自己的子模块以及何时使用符号链接?也许您应该添加一个示例来说明您的思路。
安德烈·卡隆(

0

我将其扁平化,以使子模块深度只有一个,并具有一个存储库,该存储库将所有模块作为子模块保存,除了README和构建脚本外没有其他内容。每个链接其依赖关系的软件包都有一个单独的构建脚本。否则,您可以有一个单独的软件包回购。


1
我不确定在我的帖子中是否清楚,但是我有多个应用程序依赖于相同的库,并且我不想在整个应用程序中复制库的构建脚本。
安德烈·卡隆

3
您应该详细说明您的答案,以说明其如何解决不同的问题。我还不清楚您如何链接依赖项,因为依赖于上下文,依赖库不在同一位置。
安德烈·卡隆

0

我不会使用子模块。

这很诱人,与svn-externals一样。但是,您可以确定所链接的所有这些项目仍在一年之内吗?五分之类的呢?

因此,我只是将所有必需的依赖项复制到我的项目中。这意味着只要我的仓库有效,我就可以签出确切的状态。

基本上,我的文件夹结构如下:

myproject/... [sources etc]
ext/ [third-party dependencies]


e.g. ext/boost, ext/cppunit

尽管从磁盘空间的角度来看这不是很好,但是我很保证我可以检查出所记录的每个状态,只要存储库的可用性更高。

此外,还有描述了一堆关于子模块的问题在这里


我确定它们在正确的位置,因为我要维护所有它们:-)另外,由于重新分配条件,在复制项目时要小心。
安德烈·卡伦

好的,这样可以减少问题。和许可:是的,您必须要小心,但这是一个完全不同的问题。
威伯特

0

在这里面临完全相同的问题。一个解决方案可能是有一些回购libs,将举行corenetworkgraph为子模块,只是CMakeLists会告诉每个库在哪里可以找到它的依赖性。现在,每个应用程序都将libs作为子模块,并且仅使用必要的库。

每个库的测试可以通过两种方式设置:

  • 将core_testing,graph_testing,network_testing作为单独的应用程序
  • 将测试的库部署到测试服务器,并在使用cmake运行测试时找到它们

这不是使所有库对所有其他库都可用吗?
安德烈·卡隆

默认情况下,是。但这可以在libs级cmakelist中确定。如果graph不需要了解network-不要将network相关的内容传递给graphsubdir
Max
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.