首页 > 解决方案 > 从 git 拉取后,子模块处于分离的头部状态

问题描述

我在 Windows 8.1 上运行 git,并使用以下批处理脚本从上游拉取:

git pull --recurse-submodules
git submodule update --recursive --remote --init --merge
git submodule foreach "git pull || true"

我知道这是多余的,但反复试验表明,有时上述命令中的任何一个都无法获取其他命令所获取的内容,因此在与它战斗了一段时间并阅读了关于 SO 的不同答案后,我放弃并去了采用“腰带和吊带”的方法。

这工作了大约一年,直到一周前我更新到 git 版本 2.18.0.windows.1

从那时起,几乎每次调用都会导致某些子模块中的头部分离。我进入有问题的git checkout分支,正确的分支,git pull从子模块内部执行一个,一切看起来都很好,但是如果我尝试在主仓库中再次运行脚本,BAM!即使上游没有发生任何变化并且没有任何内容被拉出,相同的子模块也会进入分离的头部状态。

我知道唯一改变的是 git 版本。

我做错了什么,正确的方法是什么?

编辑:

项目已建立,子模块用于在多个存储库之间共享的文件,并且这些文件由开发人员定期更改(不,我没有发言权)。

我想要完成的是在他们的签出分支上获取存储库的最新提交,然后更新超级项目指针并推送到原点。

编辑2:

下面会做我上面的打算吗?

git pull
git submodule foreach "git pull || true"

标签: gitgit-detached-head

解决方案


子模块应该是分离的,几乎在任何时候(尽管有一些特定的例外)。如果他们不是以前,那就错了。

请注意,这git submodule update --remote --merge基本上等同于:

(cd $submodule; git checkout $superproject_hash; git fetch ${remote};
 target_hash=$(git rev-parse $remote/$branch; git merge $target_hash)

那是:

  1. 在超级项目命令的提交中,确保子模块处于分离的 HEAD 模式。
  2. 从子模块的远程(通常是它的origin)获取,以便我们获得新的提交和更新$remote/$branch,在哪里$branch是超级项目中记录的分支。
  3. 发现更新后的远程跟踪名称的哈希 ID $remote/$branch(由我们刚刚运行的 fetch 更新)。
  4. git merge在当前 detached 上运行,以将当前HEAD快进HEAD到目标提交,或者与目标提交进行真正的合并,从而产生新的提交。无论哪种方式HEAD都保持分离,指向提交——如果我们做了一个新的,则指向刚刚提交的新提交,或者如果我们能够快进,则指向目标提交。

编辑:您也可以使用git submodule update --remote without --merge,这基本上等同于:

(cd $submodule; git fetch $remote; git checkout $remote/$branch)

即,在使用 更新远程跟踪名称后,将子模块的分离 HEAD 切换为当前远程跟踪名称git fetch。这可能更接近您的预期工作流程,但如果git merge使用快进,结果完全相同。


总体思路是子模块的提交哈希完全由超级项目和超级项目单独决定。这要求它们处于分离 HEAD 模式。在某些特定时间,您可能希望将子模块更新为其他提交;一旦你这样做了,那个提交和那个单独的提交,就是正确的提交,所以你将在超级项目中记录任何和所有新的哈希 ID,并且子模块将在这些特定的提交上再次处于分离的 HEAD 模式,并且只有这些特定的提交——这继续要求它们处于分离头模式。

(有那么一刻,你在子模块中,更新了,你可能想暂时没有人在看,秘密地,短暂地,在一个分支上......但是,哇,让我们回到分离头模式快在任何人看到之前!这就是 Git 对子模块的态度。如果你想在那个子模块中开发,好吧,Git 不会阻止你,但这只是为了让闭门造车的成年人同意!)


推荐阅读