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,我们放置两个东西:
- 有一个名为
.gitmodules
的文件(在每次提交中,因为文件总是在 Git 中,所以它在 current 提交中,无论您在 R 中签出的提交)。该文件列出了超级项目 Git 需要 运行 git clone
到 create S.
- 在每个提交 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首先克隆 (S 的 origin
) 并查看他们有哪些 S 没有的新提交。这些新提交进入您在本地拥有的 S 克隆。它们尚未使用,但现在它们存在了。 git fetch
操作还会更新克隆 S[=216 中的各种 远程跟踪名称 ,例如 origin/main
和 origin/xyzbranch
=].
既然已经完成了,Git运行ning代表R执行:
git rev-parse origin/main
或您选择的任何其他名称,以通过哈希 ID 找出提交 S 的 origin
的 main
标识的内容。该哈希 ID,无论它是什么,现在通常用于:
git switch --detach <hash>
所以 S 的当前提交现在是他们 origin/main
或其他任何人找到的提交。
该提交已在 S 中检出,但未在 R 中任何地方列出。 运行 git submodule status
或 git 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 --remote
从 R 更新你的 S 从 S的origin
。请注意,其中 none 与 R 本身有任何关系,您不必选择 S 提交做 git submodule update --remote
。由于S是一个仓库,所以可以进入子模块:
cd path/to/submodule
并且您现在在 S 的 Git 中操作,而不是 R 中的 Git .您现在可以在任何普通存储库中执行任何操作,因为您在任何普通存储库中。只是这个普通的存储库也充当了一个子模块。所以一旦你得到 S 到你喜欢的提交——即使你必须 make 这个提交——你可以弹出回存储库 R 和 git add path/to/submodule
来记录新的哈希 ID。
不过请记住,如果您在 R 和 git push
中进行新提交,则提交到 R的 origin
,其他人可以从该(第四个)Git 存储库获取新提交到他们的(第五个)Git 存储库,该存储库是 R[=216 的克隆=].到目前为止没有问题,但是如果他们现在检查你的新提交,你刚刚提交的那个提交说他们应该控制他们的 S 通过检查您在 您的 S 克隆中所做的提交来克隆。如果您尚未将此提交发送到他们可以 找到 的某个地方,他们现在将在 they 运行 git submodule update
在 他们的 R 克隆中。
(至此,根据您使用的子模块数量,我们最多可以克隆 6 个或 8 个甚至 42 个,这非常令人困惑。关键是要记住超级项目 - 上述表示法中的 Rs——通过原始哈希 ID 在其 S 子模块中调用提交,这意味着任何克隆子模块的人都需要 get 具有该哈希 ID 的提交,这意味着您通常需要在子模块中 git push
在超级项目中 git push
之前。因为我们对任何存储库所做的只是添加新的提交—我们从不 运行 git reset
或 git push --force
或 git rebase
,对吗?—这总是有效的。好吧,直到我们开始使用重置、变基和强制推送,或者忘记限制。)
好吧,这个让我发疯。我尝试 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,我们放置两个东西:
- 有一个名为
.gitmodules
的文件(在每次提交中,因为文件总是在 Git 中,所以它在 current 提交中,无论您在 R 中签出的提交)。该文件列出了超级项目 Git 需要 运行git clone
到 create S. - 在每个提交 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首先克隆 (S 的 origin
) 并查看他们有哪些 S 没有的新提交。这些新提交进入您在本地拥有的 S 克隆。它们尚未使用,但现在它们存在了。 git fetch
操作还会更新克隆 S[=216 中的各种 远程跟踪名称 ,例如 origin/main
和 origin/xyzbranch
=].
既然已经完成了,Git运行ning代表R执行:
git rev-parse origin/main
或您选择的任何其他名称,以通过哈希 ID 找出提交 S 的 origin
的 main
标识的内容。该哈希 ID,无论它是什么,现在通常用于:
git switch --detach <hash>
所以 S 的当前提交现在是他们 origin/main
或其他任何人找到的提交。
该提交已在 S 中检出,但未在 R 中任何地方列出。 运行 git submodule status
或 git 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 --remote
从 R 更新你的 S 从 S的origin
。请注意,其中 none 与 R 本身有任何关系,您不必选择 S 提交做 git submodule update --remote
。由于S是一个仓库,所以可以进入子模块:
cd path/to/submodule
并且您现在在 S 的 Git 中操作,而不是 R 中的 Git .您现在可以在任何普通存储库中执行任何操作,因为您在任何普通存储库中。只是这个普通的存储库也充当了一个子模块。所以一旦你得到 S 到你喜欢的提交——即使你必须 make 这个提交——你可以弹出回存储库 R 和 git add path/to/submodule
来记录新的哈希 ID。
不过请记住,如果您在 R 和 git push
中进行新提交,则提交到 R的 origin
,其他人可以从该(第四个)Git 存储库获取新提交到他们的(第五个)Git 存储库,该存储库是 R[=216 的克隆=].到目前为止没有问题,但是如果他们现在检查你的新提交,你刚刚提交的那个提交说他们应该控制他们的 S 通过检查您在 您的 S 克隆中所做的提交来克隆。如果您尚未将此提交发送到他们可以 找到 的某个地方,他们现在将在 they 运行 git submodule update
在 他们的 R 克隆中。
(至此,根据您使用的子模块数量,我们最多可以克隆 6 个或 8 个甚至 42 个,这非常令人困惑。关键是要记住超级项目 - 上述表示法中的 Rs——通过原始哈希 ID 在其 S 子模块中调用提交,这意味着任何克隆子模块的人都需要 get 具有该哈希 ID 的提交,这意味着您通常需要在子模块中 git push
在超级项目中 git push
之前。因为我们对任何存储库所做的只是添加新的提交—我们从不 运行 git reset
或 git push --force
或 git rebase
,对吗?—这总是有效的。好吧,直到我们开始使用重置、变基和强制推送,或者忘记限制。)