【问题标题】:Why does git say 'Changes not staged for commit' and indicate the submodule folder?为什么 git 说“更改未暂存以进行提交”并指示子模块文件夹?
【发布时间】:2019-01-28 17:27:26
【问题描述】:

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

假设我对子模块进行了更改(编辑:并提交这些更改),然后导航回父模块并执行git add -A,然后执行git status,它显示“未为提交暂存更改:子模块目录名称 ...等”。

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

【问题讨论】:

  • "edit: and do not commit those changes": 是的,这就是git status --ignore-submodules=dirty的重点:忽略子模块的状态,这是“脏的”,因为你没有在子模块。
  • 这是一个准确的理解吗:父 git 跟踪它“应该”符合的子模块的哪个 SHA1 提交;如果在子模块中进行了更改并在子模块中提交,那么父级中的git status 将警告您子模块有提交,您尚未“确认”为父级 - 即使父级仍然可以看到已签出的子模块的版本。直到你真正在 parent repo 中提交以告诉 git“继续并更新最新子模块版本的提交哈希”。?
  • 是的:当你在子模块中提交时,你需要推送,然后回到父仓库,添加,提交和推送:你将添加修改后的“gitlink”(SHA1参考)子模块及其新提交。
  • 但是当您只是修改子模块中的文件(不提交任何内容)时,git 状态(在父仓库中完成)显示的是您有一个处于“脏”状态的子模块:在其他换句话说,您不知道您的父 repo 是否编译或工作,因为在子模块中完成了那些本地未提交的更改。或者它是否可以在没有这些本地更改的情况下编译/工作。
  • 所以 dirty 表示子模块有未提交的更改?但这与仅在子模块中提交子模块的更改但父仓库仍然 not 更新到最新版本并没有太大不同 - 子模块可能不称为 dirty在后一种情况下,但在这两种情况下,您都不知道父 repo 是否会编译/工作等。

标签: git git-submodules


【解决方案1】:

这里发生的情况是,您的子模块存储库与超级项目中记录的哈希 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 存储库,称为 superproject,它控制着第二个存储库,称为 submodule。超级项目实际上有三个单独的控制旋钮,每个提交中都有一个,因此也可以在 index 中找到(因为索引控制将进入 next 提交)。

其中一个控制旋钮就是您提到的文件.gitmodules。如果子模块还不是git cloned,它会告诉超级项目如何克隆子模块。一旦子模块被克隆,它的主要工作就完成了。

第二个是您的.git/config 文件。它包含从.gitmodules 文件复制的信息,如果.gitmodules 文件不完全适合您自己的目的,您可以根据需要更新这些信息(这可能与负责.gitmodules 文件的人不同) . .git/config 中的任何设置都会覆盖.gitmodules 中的设置。否则这两个放置设置的地方基本上是等价的。

最后一个是导致问题的原因。为了使子模块签出到您的工作树中,从而对您有用,控制超级项目的 Git 启动了第二组 Git 命令。一般来说,您可能会运行:

git submodule update --init

检查子模块(如果您使用git clone --recursive,Git 会为您执行此操作)。

此时,超级项目 Git 已经创建了一个几乎为空的目录,并且路径正确。 (该目录包含一个.git 文件,命名克隆存储库的路径,或者在过去或使用旧样式向后兼容模式,包含实际的.git 目录本身。)将超级项目Git chdirs 放入这个目录并告诉子模块 Git:

  • 运行git checkout <em>hash</em>

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

特别是,子项目有自己的HEAD。这个HEAD 现在分离,并且子模块的存储库的当前提交是hash,所以它在子模块的索引和工作树中,它是当然是我们想要的:子模块的工作树是超级项目中所有子模块文件所在的路径。

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

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

如果您手动导航到子模块,并 git checkout 分支名称或任何其他按哈希 ID 提交的提交,则子模块存储库的 HEAD 会更改。它要么附加到分支名称,要么指向另一个提交,仍然处于 detached-HEAD 模式。

此时,子模块和超项目不同步。超级项目 Git 还没有对此做任何事情。你可以控制,你可以选择你想要的提交。你甚至可以做出新的提交并 git push 将它们提交到上游。一旦你完成了所有你想要的提交和git checkout-ing,并且一切都安排好了,你应该爬出子模块工作树回到你的超级项目。

现在git statusgit diff 默认情况下——这里也有很多控制旋钮——告诉你超级项目正在调用一些哈希H,但子模块有一些其他散列 S 已签出。 (如果您为此设置了控制旋钮,他们可能会或可能不会告诉您子模块本身是否需要提交。)如果您希望记录下一个超级项目提交,在该子模块的 gitlink 中,这个新的提交哈希 S,你跑:

git add path-to-submodule

(或git add -A应该做同样的事情,这就是为什么这令人费解)。这将更新索引中的 gitlink 以记录哈希 ID S 而不是 H,以便下一个超级项目提交将在 git submodule update 命令上告诉子模块 Git : 签出提交 S,作为你分离的 HEAD.

一旦超级项目中的索引与实际签出子模块中的HEAD 匹配,子模块将不会列在更改未暂存以进行提交部分。如果索引中 gitlink 中的哈希与 HEAD 中 gitlink 中的哈希不匹配,git status 要提交的更改中列出子模块的路径。

【讨论】:

  • 使用子模块签出后似乎无法返回并签出主模块,并且我将尝试的每个提交都失败了,因为文件未被跟踪。
  • @Mahdi:如果您可以将问题缩小到minimal reproducible example,您可能会找到现有的 StackOverflow 答案,如果没有,您将有示例作为新问题发布。
【解决方案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 层面的错误设计;报告未跟踪和 unignored path 作为潜在的错误提醒用户是件好事, 但目前的“status”和“diff”这样做没有多大意义 对我来说。

【讨论】:

    猜你喜欢
    • 2020-12-15
    • 1970-01-01
    • 1970-01-01
    • 2014-08-16
    • 2014-08-01
    • 2011-02-27
    • 2019-01-17
    • 2012-08-26
    • 2019-05-06
    相关资源
    最近更新 更多