为什么git会说“未暂缓提交更改”并指示子模块文件夹?

时间:2019-01-28 04:44:20

标签: git git-submodules

我在另一个模块中有一个git子模块,是通过git submodule add <...>(从父存储库发出的命令)添加的,因此.gitmodules文件是在父存储库中自动生成的。

假设我对子模块进行了更改(编辑:,并且提交这些更改),然后返回到父级并执行git add -A,然后git status ,上面写着“未上演提交更改:子模块目录名称 ...等”。

我以为git会读取.gitmodules文件(由父git生成!),实现它的git子模块目录,因此当我向父文件询问其状态时不提及其未暂存状态?

2 个答案:

答案 0 :(得分:3)

这里发生的是您的子模块存储库位于与超级项目中记录的哈希ID不同的提交上。在超级项目中运行的git status会告诉您这一点,而无需更改它,而git add -A显然也没有更改。

这最后一部分似乎是错误的。当我做类似的事情,然后使用git add -A时,我得到:

Changes to be committed:
(use "git reset HEAD <file>..." to unstage)

        modified:   [submodule path]

如果我随后再运行两个命令,它会按照我的预期返回:

$ git reset
Unstaged changes after reset:
M       [submodule path]
$ git submodule update
Submodule path [path]: checked out '[hash]'
$ git status
On branch ...
nothing to commit, working tree clean

(我怀疑您在子模块中进行了一些更改,但从未在此处进行过更改。)

这是怎么回事,细粒度的细节可以让您诊断问题

我们有一个名为 superproject 的Git存储库,它控制着另一个名为 submodule 的存储库。超级项目实际上具有三个单独的控制旋钮,每个提交中都存在一个控制旋钮,因此也可以在 index 中找到(因为索引控制着要进入 next 提交)。

您提到的文件.gitmodules是这些控制旋钮之一。如果子模块尚未git clone d,它将告诉超级项目如何克隆子模块。克隆子模块后,其主要工作就完成了。

第二个是您的.git/config文件。它包含从.gitmodules文件中复制的信息,如果.gitmodules文件不完全适合您的目的(可能与负责{ {1}}文件)。 .gitmodules中的任何设置都会覆盖.git/config中的设置。否则,这两个放置设置在本质上是等效的。

最后一个是导致问题的原因。为了使子模块能够在您的工作树中检出,从而对您有用,控制超级项目的Git旋转了第二组Git命令。通常,您可以运行:

.gitmodules

要检出子模块(尽管如果使用git submodule update --init ,Git会为您完成此操作)。

在这一点上,超级项目Git已经建立了一个具有正确路径的几乎为空的目录。 (该目录包含一个git clone --recursive文件,该文件为克隆存储库的路径命名,或者在过去或者使用旧样式的向后兼容模式时,都包含实际的.git目录本身。)超级项目Git { {1}}进入此目录并告诉子模块Git:

  • 运行.git

一旦发生这种情况,路径中就会充满从ID为 chdir 的提交中提取的文件,这大部分会使外部Git(超项目)与文件“完成”。但是有一个副作用,因为子模块本身就是一个完整的Git存储库,包含了这一切。

尤其是,子项目具有自己的git checkout hash。现在hash已被分离,并且子模块的存储库的当前提交是 HEAD ,因此它位于子模块的索引和工作树中,这当然是我们想要的:子模块的工作树是超级项目中所有子模块文件所在的路径。

但是有一个有趣的问题要回答:超级项目Git在哪里获得哈希ID?答案是:它存储在每个快照中-嗯,每个快照使用子模块-在超级项目中,每个快照都具有每个文件的完整副本。为此,超级项目的索引包含类型为 gitlink 的特殊条目。

每当超级项目告诉子模块Git时,superproject索引中的gitlink条目就会告诉超级项目哪个哈希ID赋予子模块Git:签出一些特定的提交

如果您手动导航到子模块中,并且HEAD是分支名称或任何其他由哈希ID提交的提交,则子模块存储库的hash会更改。它要么成为分支名称的附件,要么指向另一个提交,仍处于分离HEAD模式。

这时,子模块和超级项目不同步。超级项目Git对此不做任何事情。您可以控制,选择所需的提交。您甚至可以进行新的提交并将它们git checkout提交到上游。完成所需的所有提交和HEAD处理并正确安排所有内容后,您应该爬出子模块工作树,回到超级项目中。

默认情况下,现在git pushgit checkout(这里也有很多控制旋钮)告诉您,超级项目正在调用一些哈希 H ,但是该子模块还有其他一些哈希 S 签出。 (如果设置了控制旋钮,它们可能也可能不会告诉您子模块本身是否需要提交。)如果希望下一个超级项目提交记录在此子模块的gitlink中,则此新的提交哈希< em> S ,您运行:

git status

(或git diff 应该做同样的事情,这就是令人困惑的原因)。这将更新索引中的gitlink以记录哈希ID S 而不是 H ,这样,下一个超级项目提交将在git add path-to-submodule 命令上告诉子模块Git:签出提交S,作为独立的HEAD

一旦超级项目中的索引与实际检出的子模块中的git add -A相匹配,该子模块就不会列在未上演提交的更改中部分。如果索引中gitlink中的哈希与git submodule update中gitlink中的哈希不匹配,则HEAD 列出要提交的更改中子模块的路径< / em>。

答案 1 :(得分:2)

  

,因此当我向父母询问其身份时,没有提及它的未登台状态吗?

除非您使用Git 1.7.2 or more,否则它将仍然报告子模块中的更改:

您可以看到original discussion (back in 2010, for Git 1.7.x) here,它通向该功能:

  

顺便说一句,我认为行动路线将使生成的git在内部保持一致,因为默认情况下,所有内容都将其工作树中具有未跟踪路径的子模块报告为肮脏。

     
      
  • 在“ git status”输出的“ Untracked”部分中,我们在超级项目中列出了一条未跟踪的路径(即运行“ git status”的路径),以提醒用户该路径可能是一个忘记添加的新文件(除非被忽略)。
      但这不会使工作树变脏。

  •   
  • 当子模块中有未跟踪的路径时:

         
        
    • 该子模块在“已更改但未更新”部分中列出。
        即使子模块的工作树不是 ,这也会使超级项目的工作树变脏。

    •   在超级项目级别的
    • git diff”输出表明子模块已修改(即,显示了“ -dirty”),但是在子模块中运行时,未显示任何更改。

    •   
  •   
     

我认为这在UI级别是错误的设计;报告未跟踪的   作为提醒用户的潜在错误的不容忽视的途径是一件好事,   但是目前的“ status”和“ diff”这样做没有多大意义   给我。