“git submodule foreach git pull origin master”和“git pull origin master --recurse-submodules”的区别是什么?

7
我有一个dotfiles存储库,其中所有的vim插件都作为子模块存储,因此当它们发生更改时很容易更新。我认为这两个命令做了同样的事情,但我注意到这不是这种情况。
我知道有几个子模块需要更新,所以我从父存储库的根目录运行了git pull origin master --recurse-submodules。它似乎遍历了每个子模块,但只从它们的来源存储库获取更新。
当我运行git submodule foreach git pull origin master时,它实际上在每个存储库中运行了git pull origin master,同时执行了获取和合并操作。
使用--recurse-submodules的意义是什么?我有点困惑它实际上试图做什么,而且我在谷歌上找到的信息有点晦涩难懂。我想也许你们聪明的人会有一个更简单的解释。
2个回答

10
该选项主要用于获取所有子模块提交,而不仅仅是像主分支一样拉取一个特定的分支,原因在于下面两个提交详细说明:(注意,在Git 2.11中修复了一个错误,请参见本答案末尾)
对于git pull,此选项是在提交7dce19d,2010年11月,git 1.7.4-rc0中引入的:

fetch/pull: 添加--recurse-submodules选项

直到现在,你必须调用"git submodule update"(不带-N|--no-fetch选项)或类似"git submodule foreach git fetch"的命令来从远程获取已填充子模块中的新提交。这可能会导致在超级项目中获取或拉取新提交后,在"git diff --submodule"(由"git gui"和"gitk"使用)的输出中出现"(commits not present)"消息,并且对于实现子模块的递归检出是一个障碍。
此外,"git submodule update"无法在断开连接时获取更改,因此很容易忘记在断开连接之前获取子模块更改,只有在后来发现它们是必需的时才能发现。
此补丁添加了"--recurse-submodules"选项,以递归地从子模块的.git/config配置的url中获取每个填充的子模块,该选项将在每次"git fetch"的末尾或在超级项目中执行"git pull"期间使用。子模块路径来自索引。

提交 88a2197(2011年3月,git 1.7.5-rc1)解释得更详细:

fetch/pull:必要时递归到子模块中

为了能够访问由超级项目引用的所有填充子模块的提交,只有在超级项目记录了新提交时,才需要让“git fetch”递归到子模块中。当使用“--submodule”选项执行“git diff”时(自1.6.6起,“git gui”和“gitk”使用此选项),拥有这些提交非常有用,因为可以访问创建描述性输出所需的所有子模块提交。此外,合并子模块提交(自1.7.3以后添加)取决于相关的子模块提交是否存在。最后但并非最不重要的是,这使得在使用子模块时可以进行断开连接的操作,因为所有必要的提交都将被自动获取,以便成功地执行“git submodule update -N”。因此,我们选择将此模式作为fetch和pull的默认模式。
git pull origin master --recurse-submodules 
git submodule foreach git pull origin master

第一个应该拉取而不仅仅是获取,并且与第二个等效。也许这是一个参数顺序问题:
git pull --recurse-submodules origin master 

然而,这并不是更新给定分支的子模块的推荐方法:请参见下一节。
请注意,实际从主分支拉取的正确方法是将主分支注册为子模块,使该子模块跟踪主分支:
git config -f .gitmodules submodule.<path>.branch <branch>

那么一个简单的 git submodule update --remote --recursive 就足够了。
而要获取/拉取的分支记录在父仓库中(在 .gitmodules 文件中),所以你甚至不必记住你想让子模块更新到哪个分支。


更新 Git 2.11 (2011 年第四季度)

当一个子模块的“.git”存储库损坏时,会导致一些递归进入子模块的命令无限循环。

请参见提交 10f5c52(由Junio C Hamano (gitster)于 2016 年 9 月 1 日提交)。 (由Junio C Hamano -- gitster --合并于提交 293c232,2016 年 9 月 12 日)

这最后的2016次提交是在Git 2.21 (2018年第四季度)中扩展的: "git fetch --recurse-submodules"(man)可能无法获取与超级项目绑定的必要提交,这正在得到纠正。

请看commit be76c21 (2018年12月6日), 以及commit a62387b, commit 26f80cc, commit d5498e0, commit bcd7337, commit 16dd6fe, commit 08a297b, commit 25e3d28, commit 161b1cf (2018年11月28日),作者为Stefan Beller (stefanbeller).
(由Junio C Hamano -- gitster --commit 5d3635d合并,2019年1月29日)

submodule.c: 在子模块的git目录中获取内容,而不是在工作树中获取

由Stefan Beller签署

保留10f5c52656中介绍的属性 ("submodule: avoid auto-discovery in prepare_submodule_repo_env()", 2016-09-01, Git v2.11.0-rc0 -- merge listed in batch #1),通过固定子模块的git目录来实现。

但是... "git fetch"(man) 在嵌套的子模块中无法正确工作,其中最内层不感兴趣的子模块在上游进行了更新,这已经在Git 2.30(2021年第一季度)得到了纠正。

请看提交 1b7ac4e(2020年11月12日)由Peter Kaestle (dscho)进行。
(由Junio C Hamano -- gitster --提交d627bf6中合并,2020年11月25日)

子模块:修复非初始化子仓库获取的回归问题

签署者:Peter Kaestle

一项回归被 a62387b 引入了 ("submodule.c: fetch in submodules git directory instead of in worktree", 2018-11-28, Git v2.21.0-rc0 -- merge listed in batch #4)。触发它的场景是当一个远程仓库中有一个嵌套的子仓库,例如这样:superproject/middle_repo/inner_repo。个人 A 和 B 都克隆了它,但是个人 B 没有使用 inner_repo,因此没有在他的工作副本中初始化它。现在,个人 A 对 inner_repo 进行了更改,并通过 middle_repo 和超级项目进行了传播。一旦个人 A 推送了更改,个人 B 想要在超级项目级别上使用 "git fetch"(man) 获取它们,B 的 git(man) 调用将返回错误,说:“无法访问子模块 'inner_repo',在获取子模块时出错:> middle_repo”。预期情况是,在这种情况下,内部子模块将被识别为未初始化的子仓库,并且将被 git fetch(man) 命令跳过。在 'a62387b ("submodule.c: fetch in submodules git directory instead of in worktree", 2018-11-28, Git v2.21.0-rc0 -- merge listed in batch #4)' 之前,这个功能正常工作。从 a62387b 开始,代码想要在 .git/modules 中评估 "is_empty_dir()",对于一个只存在于工作树中的目录,当然会返回错误的返回值。此补丁撤消了 a62387b 的更改,并引入了回归测试。

警告:早期尝试修复 "git fetch --recurse-submodules"(man) 破坏了另一个使用情况;在找到更好的解决方案之前,请使用 Git 2.30(2021年第一季度)将其恢复。

请看提交 7091499(2020年12月2日)由Junio C Hamano (gitster)提交。
(合并于提交 f3e5dcd,作者为Junio C Hamano -- gitster --,时间为2020年12月3日)

撤销“子模块”:修复对未初始化的子子仓库获取的回归问题

这是一个回滚操作,提交 1b7ac4e6d4d490b224f5206af7418ed74e490608被撤销。Ralf Thielow报告说使用设置了submodule.recurse(手册)git fetch可能会导致子模块被错误地无限递归获取。


在Git 2.30.1(2021年第一季度)中, "git fetch --recurse-submodules"(man)修复了问题(第二次尝试)。

查看 提交505a276 (2020年12月9日) 由 Peter Kaestle (dscho) 提交。
(合并自 Junio C Hamano -- gitster --提交c977ff4,2021年1月6日)

子模块: 修复获取未初始化的子仓库时的回归问题

签名:Peter Kaestle
抄送:Junio C Hamano
抄送:Philippe Blain
抄送:Ralf Thielow
抄送:Eric Sunshine
评审者:Philippe Blain

一个回归错误由a62387b引入(“submodule.c:在子模块git目录中获取而不是在工作树中获取”,2018年11月28日,Git v2.21.0-rc0-- 合并列在批次#4中)。
触发它的情况是当一个仓库有一个子模块内嵌在另一个子模块中时,就像这样:superproject/middle_repo/inner_repo 人员A和B都拥有它的克隆版本,而人员B没有使用inner_repo,因此在他的工作副本中没有初始化它。
现在,人员A引入了对inner_repo的更改,并通过middle_repo和超级项目传播它。
一旦人员A推出更改,人员B想要使用“git fetch手册在超级项目级别上获取它们,B的git调用将返回错误,说:
无法访问子模块'inner_repo',子模块获取期间出错:middle_repo
预期情况是,在这种情况下,内部子模块将被识别为未初始化的子模块,并被git fetch命令跳过。
这在'a62387b(“submodule.c:在子模块git目录中获取而不是在工作树中获取”,2018年11月28日,Git v2.21.0-rc0-- 合并列在批次#4中)之前可以正常工作。
a62387b开始,代码希望在.git/modules内评估"is_empty_dir()",对于只存在于工作树中的目录,然后提供错误的返回值。
此补丁通过连接实际的工作树和未初始化子模块的名称来确保is_empty_dir()获得正确的未初始化子模块路径。
第一次尝试修复这个回归错误,在1b7ac4e("submodules: fix of regression on fetching of non-init subsub-repo",2020年11月12日,Git v2.30.0-rc0-- 合并列在批次#8中),通过简单地恢复a62387b,导致了在未初始化子模块的情况下递归获取超级项目时无限循环获取子模块,因此该提交在7091499中被撤消(Revert "submodules: fix of regression on fetching of non-init subsub-repo",2020年12月2日,Git v2.30.0-rc0-- 合并列在批次#10中)。
为了防止未来的故障,还要为此场景添加回归测试。

"git fetch --recurse-submodules from``"(man) 是用于从多个远程仓库 (可以是一个远程组或"--all") 中获取子模块内容的命令。在 Git 2.37 (Q3 2022) 版本中,修复了在子模块中使用该命令时会造成额外的 "git fetch"(man) 的问题。

请查看提交 0353c68(2022年5月16日)由Junio C Hamano(gitster提交。
(已合并Junio C Hamano -- gitster --提交fa61b77中,2022年5月25日)

fetch:不要从子模块运行冗余的获取操作

评审者:Glen Choo


注意:本翻译仅供参考,如有不妥之处请联系专业人士核对。
7dce19d(“fetch/pull: Add the --recurse-submodules option”,2010-11-12,Git v1.7.4-rc0 -- merge)引入了“--recurse-submodule”选项时,采取的方法是仅在所有主要获取(通常可以从单个远程获取,但也可以使用fetch_multiple()从一组远程获取)成功后执行子模块中的获取操作。
稍后我们添加了“--all”以从所有定义的远程获取,这使事情变得更加复杂。

如果您的项目有一个子模块,并尝试运行“git fetchman--recurse-submodule --all,则会看到顶级获取操作,该操作会调用另一个获取操作以获取子模块,然后再获取相同的子模块。
除了子模块的最后一个获取操作外,其余获取操作都来自通过fetch_multiple()接口生成的“git fetch --recurse-submodulesman子进程,而最后一个获取操作来自代码末尾。

由于每次在fetch_multiple()中为顶级执行递归获取子模块,因此子模块中的最后一个获取操作是冗余的。
仅当fetch_one()与顶级的单个远程交互时才重要。

顺便说一下,在处理一组远程时存在一种优化,但在使用“--all”时缺少该优化。
在前者中,当该组变为一个时,我们使用正常的fetch_one()代码路径,而不是通过fetch_multiple()接口生成“git fetch”作为子进程。
如果发现我们只定义了一个远程,则在处理“--all”时也执行相同的操作。


另请参见未吸收的gitdirs:commit 10a0d6acommit 8eb8dcfcommit 5df5106(2021年9月9日)由Jonathan Tan (jhowtan)提交。 (由Junio C Hamano -- gitster --于2021年10月6日合并至commit 921c795)。 - VonC

2

使用--recurse-submodules有什么意义?

--recurse-submodules会递归处理子模块,包括子模块内的子模块。而git submodule foreach git pull origin master则不会,它只会处理直接的子模块。


这很有道理,但我仍然不明白为什么它似乎只获取子模块中的更新,而不是实际执行 git pull origin master - Chev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接