Git嵌套子模块和依赖项

时间:2009-09-14 02:56:02

标签: git git-submodules

假设我有四个项目名为核心 A B 超级。依赖树是这样的:

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

我希望每个项目都是独立的,也就是说,我希望能够自己签出和编译每个项目(当然每个项目都依赖于它们)。

我考虑过将每个项目映射到一个存储库,然后将依赖项与子模块一起引用,但我看到了该方法的以下问题:

  1. 当检查超级及其所有依赖项时,我最终会得到三份核心
  2. 由于子模块是完全独立的,因此这三个副本中的每一个都可能指向 Core 的不同修订版本,这将是一团糟。
  3. 所以......我错过了什么吗?我是否误解了git子模块或误用了它们?有没有其他解决方案来解决这个问题(除了诉诸二进制依赖)?

6 个答案:

答案 0 :(得分:14)

您刚刚发现Git子模块缺少重写的依赖项:

如果Super依赖Core,它对Core的依赖应该“覆盖”A和B对Core的依赖。

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

答案 1 :(得分:7)

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

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

我们能够将所有项目设置为像这样的超级项目的一部分:

Super
|-A
|-B
|-Core

项目将使用相对路径相互引用,例如../A/include.h。检查回购A将不起作用,你将不得不创建另一个“超级”回购只用于A:

AWorking
|-A
|-Core

编辑 git中这种行为的另一个原因是它无法跟踪根目录下的内容(即包含.git文件夹的文件夹上方),如果您希望您的超级项目和子项目引用相同的存储库。

答案 2 :(得分:6)

我认为这里的问题是Git的设计与您要解决的问题之间存在不匹配。

Git很适合跟踪树木。项目之间的依赖关系可以(并且可能)形成图形。树是图形,但图形不一定是树。由于您的问题是如何有效地表示图形,因此树不是工作的最佳工具。

这是一种可行的方法:

git项目有一个.gitmodules目录,它记录“提示”,说明提交可能依赖的项目,可以找到它们的位置,以及项目中应该插入的路径。 (http://osdir.com/ml/git/2009-04/msg00746.html

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

这种方法使用符号链接来打破Tree模型并构建Graph。如果我们直接在git repos中记录链接,我们将在各个项目中记录特定于我们本地设置的相对路径,并且项目不会像您想要的那样“完全独立”。因此,脚本动态构建符号链接。

我认为这种方法可能会以不合需要的方式干扰git,因为我们已经采取了希望找到一件事的路径,而是将其他东西放在那里。也许我们可以.gitignore符号链接路径。但是现在我们将这些路径写下来两次并且违反DRY。在这一点上,我们还远远没有假装使用子模块。我们可以在每个项目的其他地方记录依赖项,并保留git期望的.gitmodules文件。所以我们将构建我们自己的文件,例如.dependencies,每个项目都可以在那里声明它的依赖关系。我们的脚本会在那里看,然后去构建它的符号链接。

嗯,我想我可能刚刚描述了一个ad-hoc包管理系统,它有自己的轻量级包格式:)

megamic的建议似乎很好地使用了git子模块。我们只处理在这里跟踪Set而不是Graph,并且Set很容易适合树。一层深度的树本质上是父节点和一组子节点。

正如您所指出的,这并不能完全解决您问题中所述的问题。我们可以打破两种不同类型的“这与我们可能感兴趣的信息”:   1.来自项目版本的声明(可能是项目作者)说“我需要项目Y的X版”   2.您自己的构建设置使用的语句“我已经使用这组项目版本成功测试了整个系统”

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

据我所知,大多数优秀的包管理工具都是针对特定语言或操作系统的用户而设计的。请参阅Bundler,了解ruby世界中的'gem'包,并在Debian世界中使用'.deb'包。

如果有人知道一个良好的语言中立,操作系统中立的解决方案非常适合'多语言'(http://blog.heroku.com/archives/2011/8/3/polyglot_platform/)编程项目,我会非常感兴趣!我应该将其作为一个问题发布。

答案 3 :(得分:2)

我认为你可以像这样管理一致性:定义一个"引用"所有" Core"中的分支或一系列标记具有相同的名称。库(注意:在您的示例中只有一个" Core"库)。然后指示子项目(A,B,...)的开发人员定期升级到" Core"的参考版本。他们可以尽快。

在运行构建之前,请轻松检查" Core(s)"通过在干净,递归,超级"顶级结帐中运行这三个命令,始终在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突出显示哪些子项目没有使用"参考"核心版本。

您可以轻松扩展方法以显示差异,强制升级或做任何您喜欢的事情。

答案 4 :(得分:2)

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

您可以在此阅读我的完整解决方案: https://stackoverflow.com/a/10265084/84283

答案 5 :(得分:0)

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

子模块跟踪给定分支的给定修订,因此它们可用于提供一组一致模块的快照。

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

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

至少我是如何理解它们的,尽管与Git的大多数事情一样,它可能还有很多东西。在管理依赖关系方面,我所能说的只是另一种方式,根据我的理解,不是Git有或没有子模块的设计。