为什么 git 说 'Changes not staged for commit' 并指示子模块文件夹?
Why does git say 'Changes not staged for commit' and indicate the submodule folder?
我在另一个模块中有一个 git 子模块,通过 git submodule add <...>
添加(从 parent repo 发出的命令),因此 .gitmodules
文件在parent 回购。
假设我对子模块进行了更改(编辑:并执行 not 提交这些更改)然后导航回 parent 并执行 git add -A
然后 git status
,它说 "Changes not staged for commit: submodule dir name ... etc"。
我以为 git 会读取 .gitmodules
文件(parent git 生成的文件!),意识到它是一个 git 子模块目录,因此不是当我询问 parent 其状态时提及其未暂存状态?
and therefore not mention its unstaged status when I ask the parent for its status?
它仍然会报告子模块的变化,除非你使用(with Git 1.7.2 or more):
--ignore-submodules[=<when>]
选项:
git status --ignore-submodules=dirty
配置status.submoduleSummary
您可以看到 original discussion (back in 2010, for Git 1.7.x) here,这导致该功能:
By the way, I think that route of action would make the resulting git internally consistent in that everything by default will report submodules with untracked paths in its working tree as dirty.
In the "Untracked" section of "git status" output, we list an untracked path in the superproject (i.e. the one in which "git status
" was run) to remind the user that the path might be a new file forgotten to be added (unless of course it is ignored).
But it does not make the working tree dirty.
When you have an untracked path in a submodule:
the submodule is listed in the "Changed but not updated" section.
This also makes the working tree of the superproject dirty, even though the working tree of the submodule is not.
"git diff
" output at the superproject level shows that the submodule has modifications (i.e. "-dirty" is shown), but when run inside the submodule, there is no change shown.
I think this is a misdesign at the UI level; reporting an untracked and
unignored path as potential mistake to remind the user is a good thing,
but the current way "status
" and "diff
" does so does not make much sense
to me.
这里发生的事情是您的子模块存储库与超级项目中记录的哈希 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 存储库,称为 超级项目 ,它控制着第二个存储库,称为 子模块 。超级项目实际上有三个独立的控制旋钮,其中一个出现在每个提交中,因此也可以在 index 中找到(因为索引控制将进入 下一步提交)。
这些控制旋钮之一就是您提到的文件 .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
目录本身。)超级项目 Git chdir
s 进入这个目录并告诉子模块 Git:
- 运行
git 结帐 <em>hash</em>
一旦发生这种情况,路径就会充满从 ID 为 hash
的提交中提取的文件,这主要使外部 Git( superproject) "done" 与文件。但是有一个副作用,因为子模块本身就是一个完整的 Git 存储库,具有这意味着的一切。
特别是子项目有自己的HEAD
。这个 HEAD
现在是 分离的 并且子模块的存储库的当前提交是 hash
,因此它在索引中并且子模块的工作树,这当然是我们想要的:子模块的工作树是所有子模块文件所在的超级项目中的路径。
但是有一个有趣的问题需要回答:超级项目Git从哪里得到哈希ID?答案是:它存储在每个snapshot——好吧,每个使用子模块的快照——在超级项目中,就像每个快照都有每个文件的完整副本一样。为了实现这一点,超级项目的索引包含一个类型为 gitlink.
的特殊条目
每当超级项目告诉子模块Git时,超级项目索引中的这个gitlink条目告诉超级项目将哪个哈希ID提供给子模块Git:检查一些特定的提交。
如果您手动导航到子模块,git checkout
分支名称,或任何其他通过哈希 ID 提交的内容,子模块存储库的 HEAD
会发生变化。它要么附加到分支名称,要么指向另一个提交,仍然处于分离头模式。
此时,子模块和超级项目不同步。超级项目 Git 还没有对此做任何事情。你在控制之中,你选择你想要的提交。您甚至可以进行新的提交并将 git push
提交给某些上游。一旦你完成了你想要的所有提交和 git checkout
-ing,并正确安排了一切,你应该爬出子模块工作树回到你的超级项目。
现在 git status
和 git diff
默认情况下 — 这里也有大量控制旋钮 — 告诉您超级项目正在调用一些哈希 H,但子模块有一些其他哈希值 S 签出。 (他们可能会也可能不会告诉你子模块本身是否需要提交,如果你为此设置了控制旋钮。)如果你希望你的下一个超级项目提交记录,在 gitlink对于这个子模块,这个新的提交哈希 S,你 运行:
git add path-to-submodule
(或git add -A
应该做同样的事情,这就是为什么这令人费解)。这将更新索引中的 gitlink 以记录哈希 ID S 而不是 H,以便下一个superproject commit 将在 git submodule update
命令上告诉子模块 Git:检查提交 S,作为你分离的 HEAD.
一旦超级项目中的索引与实际签出的子模块中的 HEAD
匹配,子模块 将不会 在 中列出未为 commit 部分暂存的更改。如果索引中gitlink中的散列与HEAD
中gitlink中的散列不匹配,git status
将列出要提交的更改中子模块的路径。
我在另一个模块中有一个 git 子模块,通过 git submodule add <...>
添加(从 parent repo 发出的命令),因此 .gitmodules
文件在parent 回购。
假设我对子模块进行了更改(编辑:并执行 not 提交这些更改)然后导航回 parent 并执行 git add -A
然后 git status
,它说 "Changes not staged for commit: submodule dir name ... etc"。
我以为 git 会读取 .gitmodules
文件(parent git 生成的文件!),意识到它是一个 git 子模块目录,因此不是当我询问 parent 其状态时提及其未暂存状态?
and therefore not mention its unstaged status when I ask the parent for its status?
它仍然会报告子模块的变化,除非你使用(with Git 1.7.2 or more):
--ignore-submodules[=<when>]
选项:git status --ignore-submodules=dirty
配置
status.submoduleSummary
您可以看到 original discussion (back in 2010, for Git 1.7.x) here,这导致该功能:
By the way, I think that route of action would make the resulting git internally consistent in that everything by default will report submodules with untracked paths in its working tree as dirty.
In the "Untracked" section of "git status" output, we list an untracked path in the superproject (i.e. the one in which "
git status
" was run) to remind the user that the path might be a new file forgotten to be added (unless of course it is ignored).
But it does not make the working tree dirty.When you have an untracked path in a submodule:
the submodule is listed in the "Changed but not updated" section.
This also makes the working tree of the superproject dirty, even though the working tree of the submodule is not."
git diff
" output at the superproject level shows that the submodule has modifications (i.e. "-dirty" is shown), but when run inside the submodule, there is no change shown.I think this is a misdesign at the UI level; reporting an untracked and unignored path as potential mistake to remind the user is a good thing, but the current way "
status
" and "diff
" does so does not make much sense to me.
这里发生的事情是您的子模块存储库与超级项目中记录的哈希 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 存储库,称为 超级项目 ,它控制着第二个存储库,称为 子模块 。超级项目实际上有三个独立的控制旋钮,其中一个出现在每个提交中,因此也可以在 index 中找到(因为索引控制将进入 下一步提交)。
这些控制旋钮之一就是您提到的文件 .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
目录本身。)超级项目 Git chdir
s 进入这个目录并告诉子模块 Git:
- 运行
git 结帐 <em>hash</em>
一旦发生这种情况,路径就会充满从 ID 为 hash
的提交中提取的文件,这主要使外部 Git( superproject) "done" 与文件。但是有一个副作用,因为子模块本身就是一个完整的 Git 存储库,具有这意味着的一切。
特别是子项目有自己的HEAD
。这个 HEAD
现在是 分离的 并且子模块的存储库的当前提交是 hash
,因此它在索引中并且子模块的工作树,这当然是我们想要的:子模块的工作树是所有子模块文件所在的超级项目中的路径。
但是有一个有趣的问题需要回答:超级项目Git从哪里得到哈希ID?答案是:它存储在每个snapshot——好吧,每个使用子模块的快照——在超级项目中,就像每个快照都有每个文件的完整副本一样。为了实现这一点,超级项目的索引包含一个类型为 gitlink.
的特殊条目每当超级项目告诉子模块Git时,超级项目索引中的这个gitlink条目告诉超级项目将哪个哈希ID提供给子模块Git:检查一些特定的提交。
如果您手动导航到子模块,git checkout
分支名称,或任何其他通过哈希 ID 提交的内容,子模块存储库的 HEAD
会发生变化。它要么附加到分支名称,要么指向另一个提交,仍然处于分离头模式。
此时,子模块和超级项目不同步。超级项目 Git 还没有对此做任何事情。你在控制之中,你选择你想要的提交。您甚至可以进行新的提交并将 git push
提交给某些上游。一旦你完成了你想要的所有提交和 git checkout
-ing,并正确安排了一切,你应该爬出子模块工作树回到你的超级项目。
现在 git status
和 git diff
默认情况下 — 这里也有大量控制旋钮 — 告诉您超级项目正在调用一些哈希 H,但子模块有一些其他哈希值 S 签出。 (他们可能会也可能不会告诉你子模块本身是否需要提交,如果你为此设置了控制旋钮。)如果你希望你的下一个超级项目提交记录,在 gitlink对于这个子模块,这个新的提交哈希 S,你 运行:
git add path-to-submodule
(或git add -A
应该做同样的事情,这就是为什么这令人费解)。这将更新索引中的 gitlink 以记录哈希 ID S 而不是 H,以便下一个superproject commit 将在 git submodule update
命令上告诉子模块 Git:检查提交 S,作为你分离的 HEAD.
一旦超级项目中的索引与实际签出的子模块中的 HEAD
匹配,子模块 将不会 在 中列出未为 commit 部分暂存的更改。如果索引中gitlink中的散列与HEAD
中gitlink中的散列不匹配,git status
将列出要提交的更改中子模块的路径。