The short answer is just mostly "no". While Jenkins has a checkbox, it may not be a good idea to use it here—whether it is or not, depends on who controls the name-to-ID-mapping. Other CI systems may or may not have similar checkboxes. To see what I'm getting at here, read on.
子模块的理念是超级项目控制其子模块。我认为这部分对任何人来说既不令人惊讶也不反感。但关键在于way超级项目控制每个子模块。这部分does人们感到惊讶,原因很简单。一般来说,这是对 Git 存储库的基本误解。
People think that what matters in Git repositories are branches, or more precisely, branch names like master
and develop
. That's simply not true. These branches, for the most part, are virtually irrelevant here. For humans, these branch names serve an enormous, overriding purpose. For Git, they serve a mostly-trivial point that's equally well covered by any other name, such as a tag name, or a remote-tracking name, or refs/stash
, or HEAD@{17}
.1
在 Git 中,commit,而不是分支名称(也不是标签名称,也不是任何其他名称),是核心、本质的东西。提交是 Git 的存在的理由 https://www.merriam-webster.com/dictionary/raison%20d%27%C3%AAtre。没有提交,Git 就没有任何功能。对于提交,Git 很有用。提交实际上是通过它们的哈希 ID 来识别的,它们的真实名称是那些又大又难看的字符串,比如b5101f929789889c2e536d915698f58d5c5c6b7a
。愚蠢的事情就像readable名字,比如master
or develop
,是为了弱者,生物……人类。
当然,我们作为弱小的人类,like我们的名字。所以我们在我们的存储库中使用它们。但是,当我们有一个存储库(例如超级项目)控制另一个存储库(例如子模块)时,在这种情况下,没有人类参与。因此 Git 使用提交 ID 来控制在每个子模块中提取哪个提交哈希 ID。
So this is where the surprise comes in—except, once you understand where Git is coming from, it's not surprising at all. When you let the superproject choose the submodule commit, the superproject chooses the submodule commit by hash ID. Any branch names are irrelevant. The hash ID is precise and is always correct. Branch names are sloppy—they move, on purpose, from commit to commit, over time. One commit hash ID can have zero or more branch names that either point directly to it, or can reach it through the commit graph.2
Each and every commit you make in the superproject records the exact submodule hash ID that the submodule is expected to have checked out. Hence, when you git checkout
some commit in the superproject, you should generally immediately have each submodule do its own separate git checkout
by the hash ID specified in the superproject.3
请记住,每个子模块都是自己的 Git 存储库,因此它有自己的HEAD
、索引和工作树。子模块中的索引记录了签出到子模块工作树中的文件,并且HEAD
在每个子模块中分离头模式,记录当前签出的提交的哈希ID。这是超级项目的 Gitchooses此哈希 ID(通过将其存储在超级项目的提交中),并且子模块的 Git 负责检查此特定提交。在此过程中没有任何地方提及分支名称。分支名称无关紧要!
1The in-Git function of names, besides of course providing a crutch for weak humans, is to protect objects from being garbage collected. An object is vulnerable to collection if it is not reachable from some name. Since most commits are mostly chained together, one name tends to protect most of the commits in a repository. See also footnote 2.
2For more about reachability, see Think Like (a) Git http://think-like-a-git.net/.
3This doesn't actually happen automatically by default. You have to use git checkout --recurse-submodules
or set submodule.recurse
in your configuration. Depending on what you're doing—especially, if you're trying to update the submodules—having it happen automatically is either convenient, or extremely annoying.
那么,为什么可以首先设置分支名称呢?
正如您所指出的,.gitmodules
文件可以记录分支名称。您也可以将其复制到.git/config
(the .git/config
设置覆盖.gitmodules
设置,如果两者都设置了。)但通常,子模块根本不在分支上;它被置于分离的 HEAD 模式,如上所述。那有什么好is这个分行名称?
第一个但有些不太令人满意的答案是:这一点都不好。大多数操作只是不使用它。
第二个更令人满意的答案是:一些特殊用途的操作确实会使用它。具体来说,如果您正在更新超级项目,并且想要进行一个新的超级项目提交来记录new子模块哈希 ID,您需要某种方法来挑选新的子模块提交哈希 ID。有多种方法可以解决此问题,该名称旨在用于one这些方式。
例如,假设子模块是您无法控制的公共存储库(可能在 GitHub 上)。你刚才use它。也许每年两次,或者每天 50000 次,有人更新 GitHub 存储库。他们提交的新提交master
或他们的develop
或者其他什么,破坏你使用的一堆东西,但这不是问题,因为你的超级项目不会say“给我他们最新的master
or develop
提交”,你的超级项目说“让我提交a123456...
", and a123456...
总是相同的提交,永远,直到宇宙热寂,或者我们停止使用 Git,以先发生者为准。但是,在破坏您自己的一堆软件的同时,他们还引入了您必须拥有的一项很酷的新功能。
此时您想要做的是让您的 Git(也控制您的子模块)告诉您的子模块 Git:去给我拿他们最新的master
or develop
或者我之前记录的任何名字。自从你did记录该名称,您可以使用以下命令指示 Git 指示子模块执行此操作:
git submodule update --remote
(您可以添加一些额外的标志,例如--checkout
or --rebase
or --merge
,但我不会深入讨论这些细节 - 我现在假设您只是直接使用他们的最新版本)。您的 Git 已运行子模块 Gitgit fetch
然后按照子模块分支名称副本的指示将子模块存储库更新为最新提交。 (现在有at least所有这一切都涉及三个 Git——你的超级项目、你的子模块和 GitHub 上的 Git 存储库——所以它有点复杂。无论他们是谁,他们都可能拥有一个或多个 Git 存储库,用于控制 GitHub 存储库,但至少您不必处理这一问题。嗯,还没有。)
现在您的子模块已更新,您必须修复自己的代码,既要使用新功能,又要处理它们对您已经使用的内容所做的所有重大更改。因此,您完成所有这些工作,构建和测试您的软件 - 全部在您的本地计算机上:这里还没有涉及 CI - 并使其全部正常工作。Now你可以git add
你的改变和git add
子模块的名称。您的超级项目的索引和工作树现在全部匹配,您已准备好在超级项目中进行新的提交。
注意git add submodule-path
只是告诉你的 Git 在你的索引中记录hash ID当前在子模块 Git 存储库中签出的提交。再次,分支名称(如果有)是无关紧要的。您的子模块存储库是否位于分支上并不重要master
or develop
,或者有一个分离的 HEAD;重要的是原始提交哈希 ID.
你现在跑git commit
进行新的提交。索引中的哈希 ID(控制哪个提交将被视为子模块的“正确”提交)是您通过运行记录的提交哈希 IDgit add submodule-path
. 在这种情况下,该提交 ID 得到selected因为你跑了git submodule update --remote
早些时候。但唯一重要的是索引中的哈希 ID,它会进入新的提交。
Now你可以git push
您在超级项目 Git 存储库中进行的此提交到其他系统,例如 CI 系统。它可以git checkout
这个提交,这个提交记录了right子模块哈希 ID。
如何将其与 CI 系统结合起来,以便 CI 系统选择哈希 ID?
This is, obviously, a lot harder may be harder, depending on whether your CI system offers it as a feature.
现在您已经了解了这一切是如何构建的,您已经拥有了所需的工具。您必须让 CI 系统更新(或获取)其超级项目的克隆。该超级项目包含.gitmodules
文件、CI 系统还必须克隆的任何子模块的 URL 和路径。它可能包含也可能不包含这些子模块的某些分支名称。
CI 系统现在必须指示某个 Git(超级项目 Git 或子模块 Git)以拥有子模块 Gitgit checkout
一些提交以外已经记录为correct提交,以便超级项目不再使用 CI 系统签出的提交。换句话说,你是不再构建您提交给 CI 系统的内容。你让 CI 系统用身体部位构建了一个新的弗兰肯斯坦怪物:你的提交的主体,但从其他提交中取出的肢体你没有直接指定:相反,您允许其他人指定那里的提交。你给了你的 CI 系统一个name并告诉它询问他们,无论他们是谁,这个名字对应的哈希ID是什么。
您的 CI 系统现在可以尝试构建和使用这个弗兰肯斯坦的怪物。如果一切顺利,您的 CI 系统将需要制作一个new提交,这很像your提交,但它记录了哈希 IDit来自them——无论他们是谁,再一次——针对有问题的子模块。您的 CI 系统现在可能还需要权限才能将此提交推送到某处,除非您的 CI 系统也是您的主存储库的真实来源。