我在另一个模块中有一个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 clone
d,如何克隆子模块。一旦子模块被克隆,它的主要工作就完成了。第二个是您的
.git/config
文件。它包含从.gitmodules
文件复制出来的信息,如果.gitmodules
文件不适合您自己的目的(这可能与负责.gitmodules
文件的人不同),您可以根据需要更新这些信息。.git/config
中的任何设置都会覆盖.gitmodules
中的设置。否则这两个地方的设置基本上是等价的。最后一个是导致问题的原因。为了使子模块签出到您的工作树中,从而对您有用,控制超级项目的git将启动第二组git命令。一般来说,您可以运行:
git submodule update --init
要签出子模块(尽管如果使用
git clone --recursive
,git会为您执行此操作)。此时,超级项目git已经用正确的路径创建了一个几乎为空的目录。(该目录包含一个
.git
文件,该文件命名到克隆存储库的路径,或者在过去或使用旧式向后兼容模式时,包含实际的.git
目录本身。)超级项目gitchdir
s进入该目录并告诉子模块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 status
和git 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/