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

标签 git git-submodules

我在另一个模块中有一个git子模块,通过git submodule add <...>(从父repo发出的命令)添加,因此.gitmodules文件在父repo中自动生成。
假设我对子模块进行了更改(编辑:不提交这些更改),然后导航回父模块并执行git add -A操作,然后执行git status,它会显示“未暂存提交的更改:子模块目录名…等等”。
我以为git会读取.gitmodules文件(由父git生成!),意识到它是一个git子模块目录,因此当我向父级询问它的状态时,不要提到它的未分页状态?

最佳答案

这里发生的情况是,子模块存储库的提交与超级项目中记录的哈希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

(我怀疑您在子模块中做了一些更改,但从未在那里提交过这些更改。)
发生了什么,详细的细节会让你诊断出问题
我们有一个称为超级项目的git存储库,它控制第二个称为子模块的存储库。超级项目实际上有三个单独的控制旋钮,每个提交中都有一个,因此也可以在索引中找到(因为索引控制下一次提交的内容)。
其中一个控制旋钮是您提到的文件,.gitmodules。它告诉超级项目如果子模块还没有git cloned,如何克隆子模块。一旦子模块被克隆,它的主要工作就完成了。
第二个是您的.git/config文件。它包含从.gitmodules文件复制出来的信息,如果.gitmodules文件不适合您自己的目的(这可能与负责.gitmodules文件的人不同),您可以根据需要更新这些信息。.git/config中的任何设置都会覆盖.gitmodules中的设置。否则这两个地方的设置基本上是等价的。
最后一个是导致问题的原因。为了使子模块签出到您的工作树中,从而对您有用,控制超级项目的git将启动第二组git命令。一般来说,您可以运行:
git submodule update --init

要签出子模块(尽管如果使用git clone --recursive,git会为您执行此操作)。
此时,超级项目git已经用正确的路径创建了一个几乎为空的目录。(该目录包含一个.git文件,该文件命名到克隆存储库的路径,或者在过去或使用旧式向后兼容模式时,包含实际的.git目录本身。)超级项目gitchdirs进入该目录并告诉子模块git:
运行git checkout hash
一旦发生这种情况,路径中就充满了从id为hash的提交中提取的文件,这主要使得外部git(超级项目)“完成”文件。但是有一个副作用,因为子模块本身就是一个完整的git存储库,包含了这意味着的一切。
特别是,子项目有自己的HEAD。这个HEAD现在被分离,子模块的存储库的当前提交是hash,因此它在子模块的索引和工作树中,这当然是我们想要的:子模块的工作树是所有子模块文件所在的超级项目中的路径。
但有一个有趣的问题需要回答:超级项目git从哪里获得hash id?答案是:它存储在每个快照中,每个在超级项目中使用子模块的快照,就像每个快照都有每个文件的完整副本一样。为了实现这一点,超级项目的索引包含一个类型为gitlink的特殊条目。
超级项目索引中的这个gitlink条目告诉超级项目每当超级项目告诉子模块git:check out some special commit时,要给子模块git哪个hash id。
如果您手动导航到子模块,并且git checkout分支名称或任何其他按哈希ID提交,则子模块存储库的HEAD将更改。它要么附加到分支名称,要么指向另一个提交,仍然处于分离头模式。
此时,子模块和超级项目不同步。超级项目git还没有对此采取任何措施。你可以控制,你可以选择你想要的承诺。您甚至可以进行新的提交,并将其git push发送到某个上游。一旦完成了所有的提交和git checkout-ing,并且所有的事情都安排好了,您就应该从子模块工作树中爬回到您的超级项目中。
现在git statusgit diff将,默认情况下,这里还有大量的控制旋钮告诉您超级项目正在调用一些散列h,但是子模块签出了一些其他散列。(如果您为此设置了控制旋钮,它们可能也会告诉您子模块本身是否需要提交。)如果您希望下一个超级项目提交在该子模块的gitlink中记录此新提交哈希,则运行:
git add path-to-submodule

(或者git add -A应该做同样的事情,这就是为什么这是令人费解的)。这将更新索引中的gitlink以记录散列id s而不是h,以便下一个超级项目commit将在git submodule update命令中告诉子模块git:check out commit s,作为分离的头。
一旦超级项目中的索引与实际签出子模块中的HEAD匹配,子模块将不会在“未暂存以提交的更改”部分中列出。如果索引中gitlink中的哈希与HEAD中gitlink中的哈希不匹配,git status将在要提交的更改中列出子模块的路径。

关于git - 为什么git会说“未暂缓提交更改”并指示子模块文件夹?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54395666/

相关文章:

git - 嵌入 Git 存储库的警告,即使我使用的是子模块

git - 使用 git 对 PostgreSql 数据库进行源代码控制

git - 将远程分支提示从一个分支移动到另一个分支

c# - checkout libgit2sharp 中的子模块

git - 在 GIT 中,项目混合新旧版本子组件的最佳方式是什么?

git - 在父 git 存储库中创建多个 git 子存储库

git - 配置 git repo - Ubuntu 服务器

git - 将项目从计算机导入到 GitHub 存储库

Git - 从 Master 中分离出一段代码并使其成为一个分支

git - 自动将所有子模块添加到仓库