麻烦了解git子模块摘要

时间:2020-03-19 10:47:28

标签: git git-submodules

在了解git submodule summary的确切用途时,我遇到了一些问题?我们如何以及何时使用此命令?我也无法理解--files选项的重要性。对我来说,不使用标签似乎与使用标签相同。

此外,它与git submodule status有何不同?我非常困惑。如果有帮助,我一直在阅读命令的Documentation

非常感谢您的帮助! :)

1 个答案:

答案 0 :(得分:2)

对于git submodule status

$ git submodule status
hash-1 path-1 (describe-output-1)
hash-2 path-2 (describe-output-2)
   :
hash-n path-n (describe-output-n)

对于每个子模块路径,它告诉您在该子模块路径中检出的提交的哈希ID,以及在该子模块中运行git describe的结果。例如,如果您看到

 51ebf55b9309824346a6589c9f3b130c6f371b8f foo (v2.25.0-462-g51ebf55b93)

作为输出,但随后在git checkout v2.15.0目录中做了一个foo

(cd foo; git checkout v2.15.0)

然后再次运行它,您会看到:

+cb5918aa0d50f50e83787f65c2ddc3dcb10159fe foo (v2.15.0)

相反。 (+符号表明它不同步;请参见下文。)

哈希ID只是在每个子模块中运行git rev-parse HEAD的结果。 describe输出只是在每个子模块中运行git describe的结果。中间的路径是您需要提供给cd(更改目录)命令以从超级项目切换到给定子模块的参数。

对于git submodule summary,细节要复杂一些。不过,这基本上在每个子模块中运行git log

子模块的基础

请记住,一个子模块只不过是另一个Git存储库(一个Git称为子模块的子模块),以及 Git存储库中的一些胶水, Git称之为 superproject 。超级项目中的“胶水”由极少量的项目组成:

  • git clone子模块所需的信息,该信息存储在名为.gitmodules的文件中。仅当您首先告诉超级项目Git进行克隆时(例如,通过git submodule update --init才能使用此方法。

  • 子模块的路径名,在超级项目中显示。超级项目Git将创建一个空目录/文件夹(无论您喜欢使用哪个术语)来保存此子模块的工作树。 1

  • 提交哈希ID 。超级项目Git将使用此提交哈希ID,实际上,运行(cd path; git checkout hash)将子模块Git置于分离的HEAD 模式,并检查该特定提交。

最后两个项目存储在您在超级项目中进行的每个新提交中(并且已经存储在现有提交中)。 2 为了获取存储,路径名和提交哈希ID必须存储在Git的 index 中,因为Git从索引中进行所有新提交。

(如果您不清楚Git的 index 与您的工作树之间的区别,请参阅What's the difference between HEAD, working tree and index, in Git?What does git-rm mean by working tree and index?。)


1 在现代Git中,出现在该路径中的子模块的.git是一个普通文件,其内容将是子模块Git可以在其​​中找到存储库的路径。超级项目Git会将存储库数据库移出子模块。 Git将此称为吸收子模块。在旧版Git中,子模块将具有自己的.git目录/文件夹,其中包含子模块Git存储库数据库。

2 实际上,第一项.gitmodules文件也应该包含在所有这些提交中,但是由于它是普通文件,因此没有什么特别的:像处理任何普通文件一样使用它。由于超级项目只真正需要一次,因此在克隆子模块时,如果您无意或有意将其排除在新的提交之外,直到其他人尝试使用该提交作为开始时,您才会注意到指向超级项目的新克隆。

由于更改一个.gitmodules文件非常少,并且它像其他文件一样携带提交,因此这几乎不是问题。仅当您首先使用git submodule add以外的内容创建子模块时,这才是主要问题。


阅读gitlinks,而不是直接弄乱子模块

同时记录路径和哈希ID并准备进行下一次提交的超级项目实体称为 gitlink 。它仅存在于Git的索引中,因此很难看到。 (您可以使用git ls-files --stage转储索引内容,但这通常太冗长了。)但是它总是存在的:它说要使用 this 提交,请按一个独立的HEAD, this 子模块中的 this 哈希ID。

让我们假设路径sub(在索引中,为:sub:0:sub处有一个子模块,这里的编号是暂存槽)。在超级项目中进行提交时,此gitlink进入提交。您可以从索引中读取它:

git rev-parse :sub

或从当前提交中读取它:

git rev-parse HEAD:sub

或从任何提交中读取它:

git rev-parse <hash>:sub

从给定的提交哈希ID中获取sub的存储的gitlink哈希ID。

如果您在超级项目中运行git submodule update,则Git将根据当前索引中的任何哈希ID来执行适当的(cd sub; git checkout <hash>)。如果子模块存储库“干净”,那么git checkout将干净地签出该特定的提交。

但是每个子模块都是一个Git存储库-工作树,索引和基础存储库数据库。 可以随意使用cd subgit checkout,也可以弄脏 sub的)索引和/或其工作-树。而且,该子模块可以有自己的分支名称-这是一个Git存储库,每个Git存储库都有分支名称,对吗?例如,假设您cd sub; git checkout master。现在,该子模块位于分支上的 中,而不处于分离式HEAD 模式下。您可以进行新的提交,运行git merge和/或运行所有其他命令。您可以从某个上游存储库中获取新的提交。您可以做任何您想做的事情:这是一个Git存储库,其中提供了所有Git命令。

那么,假设您已经对充当某些超级项目的子模块的某些Git存储库做了什么—并不重要。现在,您回到超级项目(cd ..),然后在超级项目中 ,您询问:您建议检出哪个提交?从超级项目的索引或提交中读取超级项目中的 gitlink 条目。

您有两个哈希ID。他们可能是一样的!也许子模块中的master存储在超级项目的gitlink中的哈希ID。或者,也许他们不同。如果您刚才在子模块中进行了新的提交,则它们肯定是不同的,因为每个新的提交哈希ID都是唯一的。

如果两者不同,git submodule status将打印+<hash>;它打印的哈希是在sub中实际检出的哈希。如果两者相同,则将打印(单个)哈希ID,而不包含+

同时,如果您运行git submodule summary,则您的超级项目Git:

  • 获取推荐哈希ID
  • 获取实际签出的哈希ID
  • 使用子模块中的git log来查找哪些提交位于这两个哈希ID之间。

具体来说,它使用git log --oneline --left-right <hash1>...<hash2>(请注意--oneline和此处的三个点;它还会强制使用更多选项,但这些是关键选项)。 hash1 值是 recommended 哈希值,而 hash2 值是实际签出的值哈希。该列表的结果是显示可以从 hash1 而不是 hash2 (以<前缀)访问的提交和提交可以从 {hash2 访问,但不能从 hash1 (以>开头)访问。

(有关可达性的更多信息,请参见Think Like (a) Git。)

git submodule summary--files--cached

我也无法理解--files选项的重要性

--files选项是默认选项。 --cached选项更改了git submodule summary获取其两个哈希ID的位置。与其从索引(:sub)中获取第一个哈希,然后进入子模块并读取第二个HEAD值,它从当前提交< / em>(HEAD:sub)并从索引(:sub中获取 second 。其其余操作相同:进入子模块并使用适当的选项运行git log