Git嵌套的子模块和依赖关系

假设我有四个名为CoreABSuper的项目 。 依赖关系树是这样的:

Super ---> Core |-> A -> Core |-> B -> Core 

我希望每个项目都是独立的,也就是说,我希望能够自行签出和编译每个项目(每个项目都有自己的依赖关系)。

我想过将每个项目映射到一个存储库,然后引用与子模块的依赖关系,但是我看到了以下问题:

  1. 当检查Super的所有依赖关系时,我最终会得到三个Core的副本。
  2. 由于子模块是完全独立的,所以这三个副本中的每一个都可能指向Core的不同版本,这将是一团糟。

所以…我错过了什么? 我误解了git submodules还是误用了它们? 有没有其他解决这个问题(除了求助于二进制依赖)?

你刚刚发现Git子模块没有重载的依赖关系:

如果Super依赖于Core,则Core的依赖关系应该“重写”A和B与Core的依赖关系。

要模仿的唯一方法就是按照你的方式创build你的超级项目,
删除A和B的子模块核心。
(意思是超级现在取决于A'和B',A'是没有核心的A,B是没有核心的B)

git仓库应该是相当primefaces的,每个仓库都是一个独立的实体用于特定目的。 除了合并项目A和B以外,超级项目的目的是什么? 如果没有任何独特的东西(即不在A,B或Core中的文件),那么它是相当多余的。

编辑:因为git submodules是特别痛苦在我工作的一个地方,我们build立了我们自己的依赖系统,通过文本文件跟踪依赖回购。 我们设置它,所以它总是跟踪分支的头,而不是一个特定的提交。

我们能够把所有的项目设置好,就好像它们是超级项目的一部分一样:

 Super |-A |-B |-Core 

项目将使用相对path(例如../A/include.h引用对方。 检查回购A将无法正常工作,您将不得不创build另一个“超级”回购工作在A:

 AWorking |-A |-Core 

编辑在git中这种行为的另一个原因是,它不能跟踪超过根回购目录(即包含.git文件夹的文件夹上面)的东西,如果你想要你的超级项目和子项目参考相同的存储库。

我认为这里的问题是Git的devise和你正在寻求解决的问题之间的不匹配。

Git对跟踪树木很有用。 项目之间的依赖关系可以(也可能)形成一个图表。 树是图,但图不一定是树。 既然你的问题是如何有效地表示graphics,树不是工作的最佳工具。

这是一种可能的方法:

一个git项目有一个.gitmodules目录,在那里它logging了“提示”,说明一个提交可能依赖哪个项目,他们可以在哪里find,以及项目中他们希望插入什么path。 ( http://osdir.com/ml/git/2009-04/msg00746.html

您可以添加一个脚本,从一组项目中读取这些信息,将每个项目的.gitmodules文件中的提示映射到实际放置这些项目的文件系统上的位置,然后添加git所期望的path中的符号链接检查子模块到相应项目的实际文件系统位置。

这种方法使用符号链接来突破树模并构build一个图。 如果我们直接在git repos中logging这些链接,那么我们将会在每个项目中logging特定于本地设置的相对path,这些项目不会像您想要的那样是“完全独立”的。 因此,脚本dynamicbuild立符号链接。

我认为这种方法可能会以不希望的方式干扰git,因为我们已经采取了希望find一件事情的path,并把其他东西放在那里。 也许我们可以.gitignore符号链接path。 但是现在我们把这些path写下来两次,违反了DRY。 在这一点上,我们已经远离假装使用子模块。 我们可以在每个项目的其他地方logging依赖关系,并保留git所期望的.gitmodules文件。 所以我们要编写自己的文件,比如.dependencies,每个项目都可以在那里声明它的依赖关系。 我们的脚本将在那里,然后去build立它的符号链接。

嗯,我想我可能刚刚描述了一个特设的包pipe理系统,用它自己的轻量级包格式:)

megamic的build议似乎是一个很好的使用git submodules给我。 我们只处理在这里跟踪一个Set而不是一个Graph,一个Set很容易放入一个Tree。 一层深的树本质上是一个父节点和一组子节点。

正如您所指出的那样,这并不能完全解决您的问题中提到的问题。 我们可以分解两种截然不同的types:“我们可能感兴趣的信息”:1.项目版本(可能是项目作者)声明“我需要项目Y版本X”2 。您自己的构build设置使用了一个声明,说“我已经成功地使用这套项目版本testing了我们的整个系统”

megamic的答案解决了(2),但是(1)我们仍然希望项目告诉我们他们的依赖关系是什么。 然后我们可以使用(1)中的信息来计算我们最终将logging为(2)的那些版本集合。 这是一个足够复杂的问题,以保证自己的工具,这使我们回到包pipe理系统:)

据我所知,大多数优秀的包pipe理工具都是为特定语言或操作系统的用户制作的。 查看Bundler在Ruby世界中的“gem”包,以及适用于Debian世界中的“.deb”包。

如果有人知道一个很好的语言中立,操作系统中立的解决scheme,非常适合“多语种”编程项目,我会很感兴趣! 我应该把它作为一个问题。

我认为你可以像这样pipe理一致性:在所有的“核心”库中定义一个“引用”分支或者具有相同名称的一系列标签(注意:在你的例子中只有一个“核心”库)。 然后指示子项目(A,B,…)的开发者尽快升级到“Core”的参考版本。

在运行构build之前,通过在一个干净的,recursion的“超级”顶级结帐中运行这三个命令,轻松地检查A,B,C,…始终如一地使用“核心”

 # 1. Switch to the reference version (= "Force" consistency where need be) git submodule foreach --recursive 'git checkout [origin/]reference || true' # 2a. Show which inconsistencies you just forced; terse output git status -s; git submodule foreach --recursive git status -s 2>/dev/null # 2b. Same but verbose output git submodule; git submodule foreach --recursive git submodule # 3. Switch back to versions individually defined by sub-projects git submodule update --recursive 

上面的“Terse输出”命令2a突出显示了哪个子项目没有使用Core的“参考”版本。

您可以轻松扩展方法来显示差异,强制升级或执行其他任何您喜欢的操作。

使用硬链接将共享子模块转换为克隆的小型实用程序任务可能有效。

您可以在这里阅读我的完整解决scheme: https : //stackoverflow.com/a/10265084/84283

我不会尝试使用子模块映射依赖关系树 – 出于您已经发现的原因。

子模块跟踪给定分支的给定修订版本,因此它们有助于给出一组一致的模块的快照。

因此,如果您的项目需要将一组不同模块的版本作为一个单元进行跟踪,则可以将它们组合在一起作为子模块。 然后,您可以在不同的版本上标记不同的模块组,以给出项目的历史logging,每个标签显示某个时间点哪些模块兼容的版本。

  tags/ release1/ |-> A@1.0 |-> B@1.1 |-> C@1.2 release2/ |-> A@2.0 |-> B@1.3 |-> C@1.5 

至less,我是如何理解他们的,虽然像Git的大多数事情一样,可能还有更多。 就pipe理依赖关系而言,我所能说的就是另辟蹊径,根据我的理解,这不是Git有或没有子模块的原因。