我有一个带有 Git 子模块的项目。它来自 ssh://... URL,位于提交 A 上。提交 B 已被推送到该 URL,我希望子模块检索该提交并对其进行更改。
现在,我的理解是 git submodule update 应该执行此操作,但事实并非如此。它不执行任何操作(没有输出,成功退出代码)。这是一个例子:
$ mkdir foo
$ cd foo
$ git init .
Initialized empty Git repository in /.../foo/.git/
$ git submodule add ssh://user@host/git/mod mod
Cloning into mod...
user@host's password: hunter2
remote: Counting objects: 131, done.
remote: Compressing objects: 100% (115/115), done.
remote: Total 131 (delta 54), reused 0 (delta 0)
Receiving objects: 100% (131/131), 16.16 KiB, done.
Resolving deltas: 100% (54/54), done.
$ git commit -m "Hello world."
[master (root-commit) 565b235] Hello world.
2 files changed, 4 insertions(+), 0 deletions(-)
create mode 100644 .gitmodules
create mode 160000 mod
# At this point, ssh://user@host/git/mod changes; submodule needs to change too.
$ git submodule init
Submodule 'mod' (ssh://user@host/git/mod) registered for path 'mod'
$ git submodule update
$ git submodule sync
Synchronizing submodule url for 'mod'
$ git submodule update
$ man git-submodule
$ git submodule update --rebase
$ git submodule update
$ echo $?
0
$ git status
# On branch master
nothing to commit (working directory clean)
$ git submodule update mod
$ ...
我也尝试过 git fetch mod ,它似乎执行了一次提取(但不可能,因为它不提示输入密码!),但是 git log< /code> 和 git show 否认新提交的存在。到目前为止,我只是rm
-ing模块并重新添加它,但这在原则上是错误的,而且在实践中很乏味。
I have a project with a Git submodule. It is from an ssh://... URL, and is on commit A. Commit B has been pushed to that URL, and I want the submodule to retrieve the commit, and change to it.
Now, my understanding is that git submodule update
should do this, but it doesn't. It doesn't do anything (no output, success exit code). Here's an example:
$ mkdir foo
$ cd foo
$ git init .
Initialized empty Git repository in /.../foo/.git/
$ git submodule add ssh://user@host/git/mod mod
Cloning into mod...
user@host's password: hunter2
remote: Counting objects: 131, done.
remote: Compressing objects: 100% (115/115), done.
remote: Total 131 (delta 54), reused 0 (delta 0)
Receiving objects: 100% (131/131), 16.16 KiB, done.
Resolving deltas: 100% (54/54), done.
$ git commit -m "Hello world."
[master (root-commit) 565b235] Hello world.
2 files changed, 4 insertions(+), 0 deletions(-)
create mode 100644 .gitmodules
create mode 160000 mod
# At this point, ssh://user@host/git/mod changes; submodule needs to change too.
$ git submodule init
Submodule 'mod' (ssh://user@host/git/mod) registered for path 'mod'
$ git submodule update
$ git submodule sync
Synchronizing submodule url for 'mod'
$ git submodule update
$ man git-submodule
$ git submodule update --rebase
$ git submodule update
$ echo $?
0
$ git status
# On branch master
nothing to commit (working directory clean)
$ git submodule update mod
$ ...
I've also tried git fetch mod
, which appears to do a fetch (but can't possibly, because it's not prompting for a password!), but git log
and git show
deny the existence of new commits. Thus far I've just been rm
-ing the module and re-adding it, but this is both wrong in principle and tedious in practice.
发布评论
评论(17)
如何更新存储库中的所有 git 子模块(两种方法可以完成两件非常不同的事情!)
快速摘要详细
信息
...代替使用 git submodule foreach --recursive git pull origin master 或 git submodule foreach --recursive git pull origin main。
在我看来,上述两个选项的最佳答案是不使用我在一些文件中看到的
--merge
和--force
选项其他答案。上面使用的选项的说明:
--init
部分会初始化子模块,以防您刚刚克隆了存储库但尚未执行此操作--recursive
会执行此操作子模块内的子模块,永远递归下去--remote
表示将子模块更新到子模块默认远程上默认分支上的最新提交。在大多数情况下,这就像为每个子模块执行 git pull origin master 或 git pull origin main 。如果您想更新到最外层存储库(超级存储库)指定的提交,请关闭--remote
。git submodule foreach --recursive git pull
(不要使用这个——它经常失败) vsgit submodule update --recursive --remote
(使用这个!--它总是有效)我在在此答案下留下了以下评论。我认为它们很重要,所以我也将它们放入我的答案中。
基本上,在某些情况下, git submodule foreach --recursive git pull 可能会起作用。对于其他人, git submodule foreach --recursive git pull origin master 可能正是您所需要的。对于其他人, git submodule foreach --recursive git pull origin main 可能就是您所需要的。而对于其他人来说,这些都可能不起作用!例如,您可能需要
git submodule foreach --recursive git pull upper upload
。或者,更糟糕的是,可能没有任何适用于您的外部存储库的 git submodule foreach 命令,因为每个子模块可能需要不同的命令来从其默认远程更新自身和默认分支。然而,在所有情况下,我可以发现这确实有效,包括对于所有情况,您可能会使用多个git子模块之一我刚刚在上面介绍过 foreach
命令。因此,请改用这个:无论如何,以下是我对此在此答案下的几点评论:
另请参阅
git 子模块
“快速入门”指南。用它来让您的团队加入。相关、&其他研究
git子模块
注释:https://github.com/ElectricRCAaircraftGuy/eRCaGuy_dotfiles#git-submodules-and-git-lfs-how-to-clone-this-repo-and-all-git-submodules-and-git-lfs-filesgit 子模块 foreach
: https://stackoverflow.com/a/45744725/4561887man git submodule
- 然后搜索 < code>foreach、--remote
等。How to update all git submodules in a repo (two ways to do two very different things!)
Quick summary
Details
...in place of using
git submodule foreach --recursive git pull origin master
orgit submodule foreach --recursive git pull origin main
.It seems to me that the best answer for both options above is to not use the
--merge
and--force
options I see in some other answers.Explanation of the options used above:
--init
part above initializes the submodule in case you just cloned the repo and haven't done that yet--recursive
does this for submodules within submodules, recursively down forever--remote
says to update the submodule to the latest commit on the default branch on the default remote for the submodule. It is like doinggit pull origin master
orgit pull origin main
in most cases, for example, for each submodule. If you want to update to the commit specified by the outer-most repo (super repo) instead, leave--remote
off.git submodule foreach --recursive git pull
(don't use this--it frequently fails) vsgit submodule update --recursive --remote
(use this!--it always works)I left the following comments under this answer. I think they are important so I am putting them in my answer too.
Basically, for some situations,
git submodule foreach --recursive git pull
might work. For others,git submodule foreach --recursive git pull origin master
might be what you need instead. For others,git submodule foreach --recursive git pull origin main
might be what you need. And for others still, none of those might work! You might needgit submodule foreach --recursive git pull upstream develop
, for instance. OR, even worse, there might not be anygit submodule foreach
command which works for your outer repo, as each submodule might require a different command to update itself from its default remote and default branch. In all cases I can find, however, this does work, including for all cases you might use one of the severalgit submodule foreach
commands I just presented above. So, use this instead:Anyway, here are my several comments about that under this answer:
See also
git submodule
"Quick Start" guide. Use it to get your team on-boarded.Related, & other research
git submodule
notes: https://github.com/ElectricRCAircraftGuy/eRCaGuy_dotfiles#git-submodules-and-git-lfs-how-to-clone-this-repo-and-all-git-submodules-and-git-lfs-filesgit submodule foreach <cmd>
: https://stackoverflow.com/a/45744725/4561887man git submodule
- then search forforeach
,--remote
, etc.这将拉取所有最新的提交。
This will pull all the latest commits.
这对我来说可以更新到最新的提交
git submodule update --recursive --remote --init
This works for me to update to the latest commits
git submodule update --recursive --remote --init
就我而言,我希望 git 更新到最新版本,同时重新填充任何丢失的文件。
以下恢复了丢失的文件(感谢
--force
,这里似乎没有提到),但它没有拉取任何新的提交:git submodule update --init --recursive --force
这做到了:
git submodule update --recursive --remote --merge --force
In my case, I wanted
git
to update to the latest and at the same time re-populate any missing files.The following restored the missing files (thanks to
--force
which doesn't seem to have been mentioned here), but it didn't pull any new commits:git submodule update --init --recursive --force
This did:
git submodule update --recursive --remote --merge --force
如果您不知道主机分支,请执行以下操作:
它将获取主 Git 存储库的一个分支,然后对于每个子模块将拉取同一分支。
If you don't know the host branch, make this:
It will get a branch of the main Git repository and then for each submodule will make a pull of the same branch.
@Jason 在某种程度上是正确的,但不完全正确。
因此,git submodule update 会签出,但它是在包含存储库的索引中进行提交。它还不知道上游有新的提交。因此,转到您的子模块,获取所需的提交并在主存储库中提交更新的子模块状态,然后执行 git submodule update 。
@Jason is correct in a way but not entirely.
So,
git submodule update
does checkout, but it is to the commit in the index of the containing repository. It does not yet know of the new commit upstream at all. So go to your submodule, get the commit you want and commit the updated submodule state in the main repository and then do thegit submodule update
.如果您希望签出每个子模块的
master
分支 - 您可以使用以下命令来实现此目的:If you are looking to checkout
master
branch for each submodule -- you can use the following command for that purpose:对我来说,所有
git子模块
都不起作用。但这是有效的:它会下载并更新第三方存储库。
然后
更新您的远程存储库(带有最后一次提交的链接
repo@xxxxxx
)。For me all
git submodule
did not work. But this worked:It downloads and thus updates the third party repo.
Then
which updates your remote repo (with the link to the last commit
repo@xxxxxx
).这是一个很棒的单行代码,可以将 master 上的所有内容更新到最新版本:
谢谢马克·贾奎斯
Here's an awesome one-liner to update everything to the latest on master:
Thanks to Mark Jaquith
处理包含子模块的 git 项目的最简单方法是始终
在每个 git 命令的末尾 添加
例如:
另一个
等等...
the simplest way to handle git projects containing submodules is to always add
at the end of each git command
example:
another
etc...
git submodule update
命令实际上告诉 Git 你想要你的每个子模块都会检查超级项目索引中已指定的提交。如果您想将子模块更新为远程可用的最新提交,则需要直接在子模块中执行此操作。总之:
或者,如果你是一个忙碌的人:
The
git submodule update
command actually tells Git that you want your submodules to each check out the commit already specified in the index of the superproject. If you want to update your submodules to the latest commit available from their remote, you will need to do this directly in the submodules.So in summary:
Or, if you're a busy person:
Git 1.8.2 提供了一个新选项
--remote
,它将实现这种行为。运行将从每个子模块的上游获取最新更改,将它们合并,并检查子模块的最新版本。正如 文档 所说:
这相当于运行 git pull每个子模块中的 (通常是
git pull origin master
或git pull origin main
),这通常正是您想要的。Git 1.8.2 features a new option,
--remote
, that will enable exactly this behavior. Runningwill fetch the latest changes from upstream in each submodule, merge them in, and check out the latest revision of the submodule. As the documentation puts it:
This is equivalent to running
git pull <remote> <default_branch>
(usuallygit pull origin master
orgit pull origin main
) in each submodule, which is generally exactly what you want.在项目父目录中,运行:
或者,如果您有递归子模块,则运行:
有时这仍然不起作用,因为在更新子模块时,您在本地子模块目录中进行了本地更改。
大多数时候,本地更改可能不是您想要提交的更改。这可能是由于子模块中的文件删除等导致的。如果是这样,请在本地子模块目录和项目父目录中进行重置,然后再次运行:
In your project parent directory, run:
Or if you have recursive submodules run:
Sometimes this still doesn't work, because somehow you have local changes in the local submodule directory while the submodule is being updated.
Most of the time the local change might not be the one you want to commit. It can happen due to a file deletion in your submodule, etc. If so, do a reset in your local submodule directory and in your project parent directory, run again:
您的主项目指向子模块应位于的特定提交。 git submodule update 尝试检查每个已初始化子模块中的提交。子模块实际上是一个独立的存储库 - 仅在子模块中创建一个新的提交并推送它是不够的。您还需要在主项目中显式添加新版本的子模块。
因此,就您的情况而言,您应该在子模块中找到正确的提交 - 让我们假设这是
master
的提示:现在返回主项目,暂存子模块并提交:
现在推送您的新项目主项目的版本:
从现在起,如果其他人更新了他们的主项目,那么他们的 git submodule update 将会更新子模块,假设它已经初始化了。
Your main project points to a particular commit that the submodule should be at.
git submodule update
tries to check out that commit in each submodule that has been initialized. The submodule is really an independent repository - just creating a new commit in the submodule and pushing that isn't enough. You also need to explicitly add the new version of the submodule in the main project.So, in your case, you should find the right commit in the submodule - let's assume that's the tip of
master
:Now go back to the main project, stage the submodule and commit that:
Now push your new version of the main project:
From this point on, if anyone else updates their main project, then
git submodule update
for them will update the submodule, assuming it's been initialized.请注意,虽然更新子模块提交的现代形式是:
请参阅 Gabriel Staples 的答案以获取替代方案, 不使用
--merge --force
。--force
选项 允许进行签出,即使包含存储库的索引中指定的提交已经与子模块中签出的提交匹配。在这种情况下,
--merge
选项似乎没有必要:“超级项目中记录的提交将被合并到子模块中的当前分支中。”旧的形式是:
除了......这第二种形式并不是真正的“安静”。
请参阅 提交 a282f5a(2019 年 4 月 12 日),作者:阮Thái Ngọc Duy (
pclouds
)。(由 Junio C Hamano --
gitster
-- 合并于 提交f1c9f6c,2019 年 4 月 25 日)Git 2.23(2019 年第 3 季度)修复了另一个问题:当“
--recursive”时,“
”选项正在使用中。git submodule foreach
”没有保护传递给要在每个子模块中正确运行的命令的命令行选项请参阅 提交 30db18b(2019 年 6 月 24 日),作者:莫里安十四行诗(
momoson
)。(由 Junio C Hamano --
gitster
-- 合并于 提交968eecb,2019 年 7 月 9 日)请注意,在 Git 2.29(2020 年第 4 季度)之前,“<代码>git子模块更新--安静“(man) 没有压制底层的“rebase”和“pull”命令。
请参阅 提交 3ad0401(2020 年 9 月 30 日),作者:西奥多·杜波依斯(
tbodt
)。(由 Junio C Hamano --
gitster
-- 合并于 提交300cd14,2020 年 10 月 5 日)在 Git 2.38(2022 年第 3 季度)中,
git-submodule.sh
已准备好转为内置函数,这意味着存在上述问题的submodule--helper
正在消失出去。请参阅 提交 5b893f7,提交 2eec463, 提交 8f12108, 提交 36d4516, 提交 6e556c4, 提交 0d68ee7, 提交 d9c7f69, 提交 da3aae9, 提交 757d092, 提交 960fad9, 提交 8577525(2022 年 6 月 28 日),作者:Ævar Arnfjörð Bjarmason (
avar
)。请参阅 提交 b788fc6(2022 年 6 月 28 日),作者:Glen Choo (
chooglen
)。(由 Junio C Hamano --
gitster
-- 合并于 提交361cbe6,2022 年 7 月 14 日)Note, while the modern form of updating submodule commits would be:
See Gabriel Staples's answer for an alternative take, not using
--merge --force
.The
--force
option allows for the checkout to take place even if the commit specified in the index of the containing repository already matches the commit checked out in the submodule.The
--merge
option seems not necessary in this case: "the commit recorded in the superproject will be merged into the current branch in the submodule."The older form was:
Except... this second form is not really "quiet".
See commit a282f5a (12 Apr 2019) by Nguyễn Thái Ngọc Duy (
pclouds
).(Merged by Junio C Hamano --
gitster
-- in commit f1c9f6c, 25 Apr 2019)And Git 2.23 (Q3 2019) fixes another issue: "
git submodule foreach
" did not protect command line options passed to the command to be run in each submodule correctly, when the "--recursive
" option was in use.See commit 30db18b (24 Jun 2019) by Morian Sonnet (
momoson
).(Merged by Junio C Hamano --
gitster
-- in commit 968eecb, 09 Jul 2019)Note that, before Git 2.29 (Q4 2020), "
git submodule update --quiet
"(man) did not squelch underlying "rebase" and "pull" commands.See commit 3ad0401 (30 Sep 2020) by Theodore Dubois (
tbodt
).(Merged by Junio C Hamano --
gitster
-- in commit 300cd14, 05 Oct 2020)With Git 2.38 (Q3 2022),
git-submodule.sh
is prepared to be turned into a builtin, meaning thesubmodule--helper
which has issues described above is being faded out.See commit 5b893f7, commit 2eec463, commit 8f12108, commit 36d4516, commit 6e556c4, commit 0d68ee7, commit d9c7f69, commit da3aae9, commit 757d092, commit 960fad9, commit 8577525 (28 Jun 2022) by Ævar Arnfjörð Bjarmason (
avar
).See commit b788fc6 (28 Jun 2022) by Glen Choo (
chooglen
).(Merged by Junio C Hamano --
gitster
-- in commit 361cbe6, 14 Jul 2022)在此讨论中似乎将两种不同的场景混合在一起:
场景 1
使用父存储库指向子模块的指针,我想检查父存储库可能指向的每个子模块中的提交第一次迭代所有子模块并从远程更新/拉取这些子模块之后。
正如所指出的,这是通过
场景 2 完成的,我认为这就是 OP 的目标
在一个或多个子模块中发生了新的事情,我想 1)拉动这些更改 2)更新父存储库以指向此/这些子模块的 HEAD(最新)提交。
这将通过
不太实用来完成,因为您必须在例如脚本中对所有 n 个子模块的 n 个路径进行硬编码,以更新父存储库的提交指针。
如果能够对每个子模块进行自动迭代,更新父存储库指针(使用 git add )以指向子模块的头部,那就太酷了。
为此,我制作了这个小 Bash 脚本:
git-update-submodules.sh
要运行它,请执行
Elaboration
首先,我假设所有存储库中都存在名为 $BRANCH (第二个参数)的分支。请随意使其变得更加复杂。
前几个部分是检查参数是否存在。然后我拉出父存储库的最新内容(每当我只是拉动时,我更喜欢使用 --ff (快进)。顺便说一句,我已经关闭了 rebase)。
然后,如果已添加新子模块或尚未初始化,可能需要进行一些子模块初始化:
然后我更新/拉取所有子模块:
注意几件事:首先,我使用
& 链接一些 Git 命令;&
- 意味着先前的命令必须执行而不会出现错误。在可能成功拉取之后(如果在远程发现新内容),我会进行推送以确保可能的合并提交不会留在客户端上。再说一次,只有当拉动确实带来了新东西时,才会发生这种情况。
最后,最后的
|| true
确保脚本在出现错误时继续执行。为了实现这一点,迭代中的所有内容都必须用双引号括起来,并且 Git 命令用括号括起来(运算符优先级)。我最喜欢的部分:
使用
--quiet
迭代所有子模块,这会删除“输入 MODULE_PATH”输出。使用'echo $path'
(必须用单引号引起来),子模块的路径将写入输出。相对子模块路径的列表被捕获在一个数组中 (
$(...)
) - 最后迭代它并执行git add $i
来更新父存储库。最后,提交包含一些消息,解释父存储库已更新。如果什么都不做,默认情况下此提交将被忽略。将其推至原点,就完成了。
我有一个脚本在 Jenkins 作业中运行此脚本,该作业链接到计划的自动部署之后,它就像一个魅力。
我希望这对某人有帮助。
It seems like two different scenarios are being mixed together in this discussion:
Scenario 1
Using my parent repository's pointers to submodules, I want to check out the commit in each submodule that the parent repository is pointing to, possibly after first iterating through all submodules and updating/pulling these from remote.
This is, as pointed out, done with
Scenario 2, which I think is what OP is aiming at
New stuff has happened in one or more submodules, and I want to 1) pull these changes and 2) update the parent repository to point to the HEAD (latest) commit of this/these submodules.
This would be done by
Not very practical, since you would have to hardcode n paths to all n submodules in e.g. a script to update the parent repository's commit pointers.
It would be cool to have an automated iteration through each submodule, updating the parent repository pointer (using
git add
) to point to the head of the submodule(s).For this, I made this small Bash script:
git-update-submodules.sh
To run it, execute
Elaboration
First of all, I assume that the branch with name $BRANCH (second argument) exists in all repositories. Feel free to make this even more complex.
The first couple of sections is some checking that the arguments are there. Then I pull the parent repository's latest stuff (I prefer to use --ff (fast-forwarding) whenever I'm just doing pulls. I have rebase off, BTW).
Then some submodule initializing, might be necessary, if new submodules have been added or are not initialized yet:
Then I update/pull all submodules:
Notice a few things: First of all, I'm chaining some Git commands using
&&
- meaning previous command must execute without error.After a possible successful pull (if new stuff was found on the remote), I do a push to ensure that a possible merge-commit is not left behind on the client. Again, it only happens if a pull actually brought in new stuff.
Finally, the final
|| true
is ensuring that script continues on errors. To make this work, everything in the iteration must be wrapped in the double-quotes and the Git commands are wrapped in parentheses (operator precedence).My favourite part:
Iterate all submodules - with
--quiet
, which removes the 'Entering MODULE_PATH' output. Using'echo $path'
(must be in single-quotes), the path to the submodule gets written to output.This list of relative submodule paths is captured in an array (
$(...)
) - finally iterate this and dogit add $i
to update the parent repository.Finally, a commit with some message explaining that the parent repository was updated. This commit will be ignored by default, if nothing was done. Push this to origin, and you're done.
I have a script running this in a Jenkins job that chains to a scheduled automated deployment afterwards, and it works like a charm.
I hope this will be of help to someone.
简单明了,获取子模块:
现在继续将它们更新到最新的主分支(例如):
Plain and simple, to fetch the submodules:
And now proceed updating them to the latest master branch (for example):