Git 子模块更新卡在旧提交

Git submodule update gets stuck at old commit

好吧,这个让我发疯。我尝试 committing/pushing/updating 一个父存储库,其子模块中的一个文件在另一个程序(XLS 电子表格)中打开。操作“成功”,仅出现 Couldn't unlink old somefile.xls 警告。

现在我正在尝试 git submodule update 并且它一直指向一个旧的提交前几步。 Git log 在子模块 main 分支上显示 HEAD 是本地和远程的最新提交,但是每当我 cd 来回到父存储库它结束 detached 在这个旧提交上。

我尝试手动更新 .git/modules/mysubmodule/HEAD 中的引用(指向这个旧提交),但显然事情不是这样进行的。我怎样才能摆脱这个令人沮丧的循环?我想对子模块进行一些微不足道的更改并进行新的提交可以修复它(尽管我尝试了一次空提交但没有运气),但我想更好地了解发生了什么,以便将来避免这种情况。

这是我的子模块 git log:

commit 713a39e531463eb9a9a608344ca39acbe520c7c4 (HEAD -> main, origin/main, origin/HEAD)

这是 git submodule update 的输出:

Submodule path 'data': checked out '7e4dc2354f5e60a8efb101a5d8a03466a911d86f'

你的错误在于认为子模块应该工作。

好的,为了对子模块和 Git 公平,让我们做到这一点:应该 自动 工作。子模块 可以 工作,但这很痛苦。 (这就是为什么有些人称它们为 sob-模块。)

问题的根源在于子模块一些其他Git存储库。此外,它通常是 third Git 存储库的 clone,您可能几乎无法控制或无法控制。每个 Git 存储库——每个 clone Git 存储库——本身就是一个孤岛。 ("No man is an island",但每个 Git 存储库都是一个。)

对于 Git 存储库 一个 子模块 ,根据定义,它必须是 其他一些 Git 存储库控制。然而,这两个涉及的Git 存储库坚称它们永远不会被控制。所以我们遇到了问题。

Git对这个问题的解决方法是这样的:

  • superproject repository R,想控制submodule 存储库 S,我们放置两个东西:

    1. 有一个名为 .gitmodules 的文件(在每次提交中,因为文件总是在 Git 中,所以它在 current 提交中,无论您在 R 中签出的提交)。该文件列出了超级项目 Git 需要 运行 git clonecreate S.
    2. 在每个提交 in R 中使用一些提交 from S,有一个实体Git调用了一个gitlink。 Git 会将此实体从提交中复制到 Git 的索引/暂存区。
  • 一旦子模块S存在——不管是你自己做的,还是让一个Git命令运行在R 创建它——我们将拥有您 运行 in R[=216= 中的 Git 命令] 运行 git switch --detach <em>hash</em> in S.

意思是R负责使用哪个commit in S您在 R 中所做的每个 提交都列出了 S 中将与该提交一起使用的确切提交哈希 ID在 R.

运行:

git submodule update

(没有其他选项)是 Git 命令控制 R 的指令,它们应该:

  • R的索引/暂存区读取S的哈希ID;
  • 运行 git switch --detach <em>hash</em> in S 使用该哈希 ID。

在您更改 那里的哈希 ID 之前,git submodule update 将继续检查该特定提交。

另一方面,运行宁:

git submodule update --remote

意味着非常不同的东西。这里,在R中运行的Git进入S和运行s:

git fetch

这会导致在 S 中运行的 Git 连接到 S 所在的 Git首先克隆 (Sorigin) 并查看他们有哪些 S 没有的新提交。这些新提交进入您在本地拥有的 S 克隆。它们尚未使用,但现在它们存在了。 git fetch 操作还会更新克隆 S[=216 中的各种 远程跟踪名称 ,例如 origin/mainorigin/xyzbranch =].

既然已经完成了,Git运行ning代表R执行:

git rev-parse origin/main

或您选择的任何其他名称,以通过哈希 ID 找出提交 Soriginmain 标识的内容。该哈希 ID,无论它是什么,现在通常用于:

git switch --detach <hash>

所以 S 的当前提交现在是他们 origin/main 或其他任何人找到的提交。

该提交已在 S 中检出,但未在 R 中任何地方列出。 运行 git submodule statusgit status in R 将显示 S 与哈希不同步R 的 index/staging-area 表示 S 应该具有的 ID。

要更新 R 中的 Git 索引,您现在必须 运行:

git add path/to/submodule

S中记录实际签出的哈希ID,在R[=的索引中216=] Git 用于 R。这还没有 提交: 就像 Git 的索引/暂存区中的任何东西一样,它只是准备好进入 next承诺你所做的。如果需要,您现在也可以更新 R 中的任何其他文件,以及 git add 那些文件,然后 运行 git commit 进行新的提交一个新的 gitlink.

new R 提交现在将调用您获得的 S 中的提交当你 运行 git submodule update --remoteR 更新你的 SSorigin。请注意,其中 none 与 R 本身有任何关系,您不必选择 S 提交做 git submodule update --remote。由于S一个仓库,所以可以进入子模块:

cd path/to/submodule

并且您现在在 S 的 Git 中操作,而不是 R 中的 Git .您现在可以在任何普通存储库中执行任何操作,因为您在任何普通存储库中。只是这个普通的存储库也充当了一个子模块。所以一旦你得到 S 到你喜欢的提交——即使你必须 make 这个提交——你可以弹出回存储库 Rgit add path/to/submodule 来记录新的哈希 ID。

不过请记住,如果您在 Rgit push 中进行新提交,则提交到 Rorigin,其他人可以从该(第四个)Git 存储库获取新提交到他们的(第五个)Git 存储库,该存储库是 R[=216 的克隆=].到目前为止没有问题,但是如果他们现在检查你的新提交,你刚刚提交的那个提交说他们应该控制他们的 S 通过检查您在 您的 S 克隆中所做的提交来克隆。如果您尚未将此提交发送到他们可以 找到 的某个地方,他们现在将在 they 运行 git submodule update 他们的 R 克隆中。

(至此,根据您使用的子模块数量,我们最多可以克隆 6 个或 8 个甚至 42 个,这非常令人困惑。关键是要记住超级项目 - 上述表示法中的 Rs——通过原始哈希 ID 在其 S 子模块中调用提交,这意味着任何克隆子模块的人都需要 get 具有该哈希 ID 的提交,这意味着您通常需要在子模块中 git push 在超级项目中 git push 之前。因为我们对任何存储库所做的只是添加新的提交—我们从不 运行 git resetgit push --forcegit rebase,对吗?—这总是有效的。好吧,直到我们开始使用重置、变基和强制推送,或者忘记限制。)