git子模块引用不显示当前分支,但显示提交哈希

时间:2018-04-04 15:09:44

标签: git git-submodules

这是我的子模块

+a3ebd2c9635f67ef7d0b430e505e40fa65f3abd4 android-libs-core (0.3-145-ga3ebd2c) 6041beecf8082b2e68b597e3305c8b9ef4012772 android-libs-debug (heads/master) df7dad74c8b922714b2f17defb8172df7ecf32ea android-libs-router (v1.1-5-gdf7dad7) 7a669bac24430841a5d86c0d90964cd612005769 android_libs_view (heads/master)

PS:即使不显示当前分支,子模块也跟踪主分支,所以我有疑虑。请参阅子模块信息。

+a3ebd2c9635f67ef7d0b430e505e40fa65f3abd4 android-libs-core (0.3-145-ga3ebd2c)

(0.3-145-ga3ebd2c)的意思是什么,它是对的?

正常情况下会显示子模块的当前分支,所以我担心我的git汇编有一些问题

1 个答案:

答案 0 :(得分:2)

首先,请注意子模块本身就是 Git存储库。作为存储库,它具有存储库所具有的一切:提交,分支名称,标记名称,索引,工作树等。与此同时,它是一个 sub 模块,以某种方式被另一个存储库包围。

Git可能应该直接支持" subrepos"。 1 它没有。 Subrepos 是我在这里用来表示除Git的子模块之外的这些(未指定的)事物的术语。我不是第一个补课的人;搜索" subrepos"会找到几个实际的实现(都是不同的)。

子模块设计为分离

Git认为 1 子模块是外部依赖项:项目之外和控制之外的东西。在您的项目中--Git现在称之为超级项目,以区别于子模块 Git存储库 - 您将记录:

  • 要克隆的存储库的URL:您将赋予git clone的参数。
  • 超级项目中克隆应该到达的位置。

这两个项目会进入存储库顶部名为.gitmodules的文件中。如果您需要多个子模块,每个子模块都有一个名称:

$ cat .gitmodules
[submodule "path/to/sub1"]
        path = path/to/sub1
        url = ssh://one.example.com/repo1.git

[submodule "path2/sub2"]
        path = path2/sub2
        url = ssh://two.example.com/repo2.git

path有点多余,因为子模块的名称是路径名,但是这些是如何布局的。在任何情况下,Git实际上会在pathcd处创建一个空目录,并将git clone URL放入最初为空的目录。 2

这里缺少一些东西。克隆存储库时,您还git checkout某些特定提交,通常使用分支名称master。这样做是为了使用该提交的文件填充该存储库的 work-tree 。 (Git实际上首先将文件复制到该工作树的索引,但索引条目很小,如果您从未在子存储库中执行任何工作,你不会在意这件事。)

那么:在其他存储库的工作树中Git应该检出 commit 吗?

1 不要将计算机拟人化 - 他们讨厌这个!

2 在旧版本的Git中,Git确实这样做了,子模块存储库与任何其他存储库都无法区分。该方法仍然有效,但现代Git现在将子模块的.git目录放在超级项目的.git目录中。它编写的.git文件包含子模块的.git容器的路径,而不是.git目录。这样就可以从子模块中发现子模块实际上是一个子模块以及一个独立的Git存储库。

提交名称

Git有很多方法来命名提交。有关完整列表,请参阅the gitrevisions documentation。您始终可以将任何这些名称转换为原始提交哈希ID。事实上,git rev-parse命令就是为了这样做: 3

$ git rev-parse master
0afbf6caa5b16dcfa3074982e5b48e27d452dbbb

我们上面已经注意到,git clonegit checkout结尾,通常是分支名称,如master。但是,您可以运行git clone -b tag-name,其中git clone运行git checkout tag-name。如果你这样做,你会发现你处于"分离的HEAD"模式。

如果您运行git clone --no-checkout,则克隆步骤根本不会运行git checkout,现在您可以使用git checkout hash-id查看具体提交。再一次,你将进入"分离的HEAD"模式。

因此,使用一个名称检查一个特定的提交,有一个重要区别,基本上相同,使用git checkout hash-id来检查名称指向的提交 - 使用名称上的git rev-parse获得的哈希ID。

当然,一个重要的区别是如果名称是分支名称,如master,Git会把你放在"上分支"而不是"分离的HEAD"模式。 (如果需要,您可以使用git checkout --detach name强制执行分离的HEAD模式。)

关于提交这些名称的重要注意事项是最后,名称只是转换为哈希ID。它是哈希ID,而不是名称, 真正识别特定提交。事实上,分支名称 - 而不是标签名称 4 - 是分支名称​​随时间变化 :它今天可能指向0afbf6caa...,但明天指向468165c1d...

因此,子模块是我们控制的Git存储库,例如用于各种功能的库。假设我们的超级项目使用该库,并使用该库的一个特定版本v1.1a123456...。我们想要记录的是:

  • 存储库的URL;
  • 要检出存储库的路径;和
  • v1.1a123456...:要检出的特定哈希ID ,作为分离的HEAD。

这样,当我们在超级项目中git checkout master时,无论我们的master哈希ID是什么,我们都希望Git在必要时git clone子项目,然后查看一个特定的提交a123456... 。这将是子模块中的一个独立的HEAD!

随着时间的推移,我们将开发自己的超级项目。我们可能需要来自库的v1.2的新功能,即提交b789abc...而不是a123456...。因此,我们将进入子模块git checkout v1.2以获取新的分离的HEAD提交,返回我们的超级项目,使所有内容与更新的子模块一起工作,然后{{1 (对于超级项目的工作树的索引)一条记录 use git add 。然后,我们会b789abc...保存新配对:新提交使用git commit

我们如何在子模块中检出b789abc...并不重要。重要的是,现在的超级项目与提交的一起工作。我们希望 superproject 提交,从这里开始说使用子模块中的特定提交。

3 b789abc...命令除了将名称转换为哈希ID之外,还可以做很多事情,但这是其主要功能之一。

4 标签名称与分支名称不同,不应更改。标记名称还具有我们选择的优势:rev-parse可能比v2.17.0更有意义。如果可以通过标签名称指定Git子模块,那可能会很好,但它们不能(至少在今天)。

结论(或者是吗?)

特定提交是分离的HEAD。超级项目使用其子模块中的特定提交。因此,超级项目在子模块中调用分离的HEAD。这就是为什么 Git的目的是以这种方式工作。

没有地址的主要内容是更新过程。我们在上面提到过,在某些时候,我们想要更新子模块中的冻结点。为此,我们将468165c1d...放入子模块工作树并开始执行像cd这样的Gitty事情,可能首先使用git checkout branchname此时名称 - 分支和/或标记名称 - 可能会变得很重要。将它们记录在某个地方的超级项目中可能会很好。

git fetch添加到子模块的超级项目branch = name条目中的原因是什么。较新版本的Git对此有一些支持:特别是.gitmodules可以(取决于选项)使用它。 documentation for this很难阅读。要记住每个子模块Git存储库都是一个Git存储库本身,以及作为一个子模块,这有很大帮助。由于存储库,因此它可以拥有自己的git submodule update远程及其自己的分支,以便运行origin更新git fetch,依此类推。

就超级项目的Git而言,子模块实际上已经检出了一个特定的提交。无论子模块Git是否处于"分离的HEAD"模式。每次进行提交时,您在超级项目中提交的提交都会记录一个特定的哈希ID。该一个哈希ID是存储在超级项目的工作树的索引中的哈希ID。 ("存储在超级项目的工作树索引中的那个"是一种满口的,更糟糕​​的是,一些检查索引的常用工具,其中没有那么多,不要对于子模块来说,它确实非常有效。)