我有一个带有Git子模块的项目。它来自ssh://…URL,在提交a上,提交B已经被推到那个URL,我想让子模块检索提交,并更改它。

现在,我的理解是git子模块更新应该这样做,但它没有。它不做任何事情(没有输出,成功退出代码)。这里有一个例子:

$ 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日志和git显示否认新提交的存在。到目前为止,我只是在rm-ing模块并重新添加它,但这在原则上是错误的,在实践中也很乏味。


git子模块update命令实际上告诉git,您希望每个子模块都检出在超项目索引中已经指定的提交。如果您希望将子模块更新为远程可用的最新提交,则需要直接在子模块中执行此操作。

总结一下:

# Get the submodule initially
git submodule add ssh://bla submodule_dir
git submodule init

# Time passes, submodule upstream is updated
# and you now want to update

# Change to the submodule directory
cd submodule_dir

# Checkout desired branch
git checkout master

# Update
git pull

# Get back to your project root
cd ..

# Now the submodules are in the state you want, so
git commit -am "Pulled down update to submodule_dir"

或者,如果你是个大忙人:

git submodule foreach git pull origin master

@Jason在某种程度上是正确的,但不完全正确。

更新 更新已注册的子模块, 即克隆缺失子模块和 方法中指定的提交 包含存储库的索引。 这将使子模块HEAD为 分离,除非——rebase或——merge是 指定或键 子模块。美元的名字。Update设置为 重组或合并。

因此,git子模块update确实签出,但它是提交到包含存储库的索引中。它根本不知道上游的新提交。所以去你的子模块,获得你想要的提交,并在主存储库中提交更新后的子模块状态,然后更新git子模块。


主项目指向子模块应该所在的特定提交。Git子模块update尝试检查每个已初始化的子模块中的提交。子模块实际上是一个独立的存储库——仅仅在子模块中创建一个新的提交并推送是不够的。您还需要显式地在主项目中添加子模块的新版本。

所以,在你的情况下,你应该在子模块中找到正确的提交-让我们假设这是master的提示:

cd mod
git checkout master
git pull origin master

现在回到主项目,阶段子模块并提交:

cd ..
git add mod
git commit -m "Updating the submodule 'mod' to the latest version"

现在推送你的主项目的新版本:

git push origin master

从这一点开始,如果有人更新了他们的主项目,那么他们的git子模块update将更新子模块,假设它已经初始化。


在你的项目父目录下,运行:

git submodule update --init

或者如果你有递归子模块运行:

git submodule update --init --recursive

有时这仍然不起作用,因为在更新子模块时,在本地子模块目录中有本地更改。

大多数情况下,本地更改可能不是您想要提交的更改。这可能是由于子模块中的文件删除等原因造成的。如果是这样,在你的本地子模块目录和你的项目父目录中重置,再次运行:

git submodule update --init --recursive

在这个讨论中,似乎有两种不同的场景被混合在一起:

场景1

使用父存储库指向子模块的指针,我想检查父存储库指向的每个子模块中的提交,可能是在第一次遍历所有子模块并从远程更新/提取这些子模块之后。

如前所述,这已经完成了

git submodule foreach git pull origin BRANCH
git submodule update

情景2,我认为这是OP的目标

新的东西发生在一个或多个子模块中,我想1)拉这些更改和2)更新父库指向这个/这些子模块的HEAD(最新)提交。

这将由

git submodule foreach git pull origin BRANCH
git add module_1_name
git add module_2_name
......
git add module_n_name
git push origin BRANCH

不是很实用,因为你必须硬编码n个路径到所有n个子模块,例如一个脚本来更新父库的提交指针。

通过每个子模块进行自动迭代,更新父存储库指针(使用git add)以指向子模块的头部,这很酷。

为此,我编写了这个小Bash脚本:

git-update-submodules.sh

#!/bin/bash

APP_PATH=$1
shift

if [ -z $APP_PATH ]; then
  echo "Missing 1st argument: should be path to folder of a git repo";
  exit 1;
fi

BRANCH=$1
shift

if [ -z $BRANCH ]; then
  echo "Missing 2nd argument (branch name)";
  exit 1;
fi

echo "Working in: $APP_PATH"
cd $APP_PATH

git checkout $BRANCH && git pull --ff origin $BRANCH

git submodule sync
git submodule init
git submodule update
git submodule foreach "(git checkout $BRANCH && git pull --ff origin $BRANCH && git push origin $BRANCH) || true"

for i in $(git submodule foreach --quiet 'echo $path')
do
  echo "Adding $i to root repo"
  git add "$i"
done

git commit -m "Updated $BRANCH branch of deployment repo to point to latest head of submodules"
git push origin $BRANCH

要运行它,执行

git-update-submodules.sh /path/to/base/repo BRANCH_NAME

细化

首先,我假设名称为$ branch(第二个参数)的分支存在于所有存储库中。你可以让这个问题变得更复杂。

前几节是检查参数是否存在。然后我拉出父库的最新的东西(我更喜欢使用——ff(快进)每当我只是做拉。顺便说一句,我已经重新调基了。

git checkout $BRANCH && git pull --ff origin $BRANCH

然后,如果新的子模块已经添加或尚未初始化,则可能需要初始化一些子模块:

git submodule sync
git submodule init
git submodule update

然后更新/拉出所有子模块:

git submodule foreach "(git checkout $BRANCH && git pull --ff origin $BRANCH && git push origin $BRANCH) || true"

注意几件事:首先,我使用&& -链接一些Git命令,这意味着前一个命令必须正确执行。

在一次可能成功的拉操作之后(如果在远程上发现了新内容),我执行一次推操作,以确保不会将可能的合并提交遗留在客户机上。同样,只有当拉力真的带来了新的东西时,才会发生这种情况。

最后,|| true确保脚本继续错误。要做到这一点,迭代中的所有内容都必须用双引号括起来,Git命令用圆括号括起来(操作符优先级)。

我最喜欢的部分:

for i in $(git submodule foreach --quiet 'echo $path')
do
  echo "Adding $i to root repo"
  git add "$i"
done

用——quiet迭代所有子模块-,这将删除' entered MODULE_PATH'输出。使用'echo $path'(必须是单引号),子模块的路径被写入输出。

这个相对子模块路径列表被捕获在一个数组中($(…))-最后迭代这个,并执行git add $i来更新父库。

最后,提交一些消息,说明父库已更新。如果什么都没有做,默认情况下这个提交将被忽略。把这个推到原点,你就完成了。

我在Jenkins作业中运行了一个脚本,该脚本随后链接到计划的自动部署,它的工作就像一个魅力。

我希望这将对某人有所帮助。


Git 1.8.2提供了一个新选项——remote,它将启用这种行为。运行

git submodule update --remote --merge

将从每个子模块的上游获取最新的更改,将它们合并到子模块中,并检出子模块的最新修订。如文档所述:

——远程 此选项仅对update命令有效。与其使用超项目记录的SHA-1来更新子模块,不如使用子模块的远程跟踪分支的状态。

这相当于在每个子模块中运行git pull <remote> <default_branch>(通常是git pull origin master或git pull origin main),这通常正是你想要的。


这里有一个很棒的一行代码,可以将所有内容更新到master上的最新版本:

git submodule foreach 'git fetch origin --tags; git checkout master; git pull' && git pull && git submodule update --init --recursive

感谢Mark Jaquith


简单地获取子模块:

git submodule update --init --recursive

现在继续将它们更新到最新的主分支(例如):

git submodule foreach git pull origin master

在我的例子中,我希望git更新到最新,同时重新填充任何丢失的文件。

下面恢复了丢失的文件(多亏了——force,这里似乎没有提到),但它没有拉出任何新的提交:

Git子模块update——init——recursive——force

这是:

Git子模块更新——递归——远程——合并——强制


git pull --recurse-submodules

这将拉出所有最新的提交。


如果你不知道主机分支,这样做:

git submodule foreach git pull origin $(git rev-parse --abbrev-ref HEAD)

它将获得主Git存储库的一个分支,然后为每个子模块提取相同的分支。


注意,虽然更新子模块提交的现代形式是:

git submodule update --recursive --remote --force

请参阅Gabriel Staples的另一种回答,不使用“合并”力。

——force选项允许发生签出,即使在包含存储库的索引中指定的提交已经与子模块中签出的提交匹配。

在这种情况下,——merge选项似乎没有必要:“记录在超项目中的提交将被合并到子模块中的当前分支中。”


旧的形式是:

git submodule foreach --quiet git pull --quiet origin

除了……第二种形式并不是真正的“安静”。

参见提交a282f5a (12 Apr 2019) by nguyThái ngeconc Duy (pclouds)。 (由Junio C Hamano—gitster—在commit f1c9f6c中合并,2019年4月25日)

子模块foreach:修复“<命令>—quiet”不被尊重

Robin reported that git submodule foreach --quiet git pull --quiet origin is not really quiet anymore. It should be quiet before fc1b924 (submodule: port submodule subcommand 'foreach' from shell to C, 2018-05-10, Git v2.19.0-rc0) because parseopt can't accidentally eat options then. "git pull" behaves as if --quiet is not given. This happens because parseopt in submodule--helper will try to parse both --quiet options as if they are foreach's options, not git-pull's. The parsed options are removed from the command line. So when we do pull later, we execute just this git pull origin When calling submodule helper, adding "--" in front of "git pull" will stop parseopt for parsing options that do not really belong to submodule--helper foreach. PARSE_OPT_KEEP_UNKNOWN is removed as a safety measure. parseopt should never see unknown options or something has gone wrong. There are also a couple usage string update while I'm looking at them. While at it, I also add "--" to other subcommands that pass "$@" to submodule--helper. "$@" in these cases are paths and less likely to be --something-like-this. But the point still stands, git-submodule has parsed and classified what are options, what are paths. submodule--helper should never consider paths passed by git-submodule to be options even if they look like one.


Git 2.23(2019年Q3)修复了另一个问题:当“——recursive”选项正在使用时,“Git submodule foreach”没有保护传递给命令的命令行选项,以便在每个子模块中正确运行。

参见莫里亚十四行诗(momoson)的commit 30db18b(2019年6月24日)。 (由Junio C Hamano—gitster—在commit 968eecb中合并,2019年7月9日)

子模块foreach:修复选项的递归

Calling: git submodule foreach --recursive <subcommand> --<option> leads to an error stating that the option --<option> is unknown to submodule--helper. That is of course only, when <option> is not a valid option for git submodule foreach. The reason for this is, that above call is internally translated into a call to submodule--helper: git submodule--helper foreach --recursive \ -- <subcommand> --<option> This call starts by executing the subcommand with its option inside the first level submodule and continues by calling the next iteration of the submodule foreach call git --super-prefix <submodulepath> submodule--helper \ foreach --recursive <subcommand> --<option> inside the first level submodule. Note that the double dash in front of the subcommand is missing. This problem starts to arise only recently, as the PARSE_OPT_KEEP_UNKNOWN flag for the argument parsing of git submodule foreach was removed in commit a282f5a. Hence, the unknown option is complained about now, as the argument parsing is not properly ended by the double dash. This commit fixes the problem by adding the double dash in front of the subcommand during the recursion.


请注意,在Git 2.29 (Q4 2020)之前,“Git子模块更新—quiet”(man)并没有压制底层的“rebase”和“pull”命令。

参见Theodore Dubois (tbodt)提交3ad0401(2020年9月30日)。 (由Junio C Hamano - gitster -在commit 300cd14中合并,2020年10月5日)

子模块更新:在merge/rebase下使用"——quiet" 署名:Theodore Dubois

Commands such as $ git pull --rebase --recurse-submodules --quiet produce non-quiet output from the merge or rebase. Pass the --quiet option down when invoking "rebase" and "merge". Also fix the parsing of git submodule update(man) -v. When e84c3cf3 ("git-submodule.sh: accept verbose flag in cmd_update to be non-quiet", 2018-08-14, Git v2.19.0-rc0 -- merge) taught "git submodule update"(man) to take "--quiet", it apparently did not know how ${GIT_QUIET:+--quiet} works, and reviewers seem to have missed that setting the variable to "0", rather than unsetting it, still results in "--quiet" being passed to underlying commands.


在Git 2.38 (Q3 2022)中,Git -submodule.sh已准备好被转换为内置,这意味着存在上述问题的子模块-helper将被淡出。

参见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)。 参见commit b788fc6(2022年6月28日),作者Glen Choo (chooglen)。 (由Junio C Hamano—gitster—在commit 361cbe6中合并,2022年7月14日)

git-submodule.sh:使用$quiet,而不是$GIT_QUIET 署名:Ævar Arnfjörð Bjarmason

Remove the use of the "$GIT_QUIET" variable in favor of our own "$quiet", ever since b3c5f5c ("submodule: move core cmd_update() logic to C", 2022-03-15, Git v2.36.0-rc0 -- merge) we have not used the "say" function in git-sh-setup.sh, which is the only thing that's affected by using "GIT_QUIET". We still want to support --quiet for our own use though, but let's use our own variable for that. Now it's obvious that we only care about passing "--quiet" to git submodule--helper, and not to change the output of any "say" invocation.


如果你想为每个子模块签出主分支,你可以使用下面的命令:

git submodule foreach git checkout master

处理包含子模块的git项目最简单的方法是总是添加

--recurse-submodules 

在每个git命令的末尾 例子:

git fetch --recurse-submodules

另一个

git pull --update --recurse-submodules

等等……


这适用于我更新到最新的提交

Git子模块更新——递归——远程——初始化


对我来说,所有的git子模块都不工作。但这个方法奏效了:

cd <path/to/submodule>
git pull

它下载并因此更新第三方回购。 然后

cd <path/to/repo>
git commit -m "update latest version" <relative_path/to/submodule>
git push

它更新了您的远程回购(与最后一次提交的链接repo@xxxxxx)。


如何在repo中更新所有git子模块(两种方法做两种完全不同的事情!)

快速的总结

# Option 1: as a **user** of the outer repo, pull the latest changes of the
# sub-repos as previously specified (pointed to as commit hashes) by developers
# of this outer repo.
# - This recursively updates all git submodules to their commit hash pointers as
#   currently committed in the outer repo.
git submodule update --init --recursive

# Option 2. As a **developer** of the outer repo, update all subrepos to force
# them each to pull the latest changes from their respective upstreams (ex: via
# `git pull origin main` or `git pull origin master`, or similar, for each
# sub-repo). 
git submodule update --init --recursive --remote

# now add and commit these subrepo changes
git add -A
git commit -m "Update all subrepos to their latest upstream changes"

细节

Option 1: as a user of the outer repo, trying to get all submodules into the state intended by the developers of the outer repo: git submodule update --init --recursive Option 2: as a developer of the outer repo, trying to update all submodules to the latest commit pushed to the default branch of each of their remote repos (ie: update all subrepos to the latest state intended by the developers of each subrepo): git submodule update --init --recursive --remote ...in place of using git submodule foreach --recursive git pull origin master or git submodule foreach --recursive git pull origin main.

在我看来,上述两个选项的最佳答案是不要使用我在其他一些答案中看到的——merge和——force选项。

上述选项的解释:

the --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 and --remote says to update the submodule to the latest commit on the default branch on the default remote for the submodule. It is like doing git pull origin master or git 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子模块foreach——递归Git pull(不要用这个——它经常失败)vs Git子模块update——递归——远程(用这个!——它总是有效的)

我在这个答案下面留下了以下评论。我认为它们很重要,所以我也把它们放在我的回答中。

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 need git submodule foreach --recursive git pull upstream develop, for instance. OR, even worse, there might not be any git 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 several git submodule foreach commands I just presented above. So, use this instead:

git submodule update --recursive --remote

不管怎样,下面是我对这个答案的一些评论:

(1/4) @DavidZ, a lot of people think that git submodule foreach git pull and git submodule update --remote are the same thing, with the latter simply being the newer command. They aren't the same thing, however. git submodule foreach git pull will fail under multiple circumstances for which git submodule update --remote works just fine! If your submodule points to a commit hash that doesn't have a branch pointing to it, which is frequently the case in real-life development where you want a particular version of the submodule for your outer repo, then that submodule... (2/4)...is in a detached HEAD state. In this case, git submodule foreach git pull fails to run git pull on that submodule since a detached HEAD cannot have an upstream branch. git submodule update --remote, however, works just fine! It appears to call git pull origin main on that submodule if origin is the default remote and main is the default branch on that default remote, or git pull origin master, for instance, if origin is the default remote but master is the default branch. (3/4) Furthemore, git submodule foreach git pull origin master will even fail in many cases where git submodule update --remote works just fine, since many submodules use master as the default branch, and many other submodules use main as the default branch since GitHub changed from master to main recently in order to get away from terms related to slavery in the United States ("master" and "slave"). (4/4) So, I added the explicit remote and branch to make it more clear that they are frequently needed, and to remind people that git pull is frequently not enough, and git pull origin master may not work, and git pull origin main may work when the former doesn't, but also may not even work, and that none of them by themselves are the same as git submodule update --remote, since that latter command is smart enough to just do git pull <default_remote> <default_branch> for you for each submodule, apparently adjusting the remote and branch as necessary for each submodule.

相关,及其他研究

如何找到一个回购的主要分支:https://stackoverflow.com/a/49384283/4561887 如何通过git子模块foreach运行自定义命令来更新每个subrepo <cmd>: https://stackoverflow.com/a/45744725/4561887 Man git子模块-然后搜索foreach,——remote等。