1025

我有一个带有 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,它似乎做了一个 fetch (但不可能,因为它没有提示输入密码!),但git log否认git show存在新的提交。到目前为止,我一直在rm-ing 模块并重新添加它,但这在原则上是错误的,在实践中也是乏味的。

4

16 回答 16

1742

git submodule 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
于 2011-04-29T05:50:56.800 回答
585

Git 1.8.2 提供了一个新选项 ,--remote它将完全启用此行为。跑步

git submodule update --remote --merge

将从每个子模块的上游获取最新更改,将它们合并,并检查子模块的最新版本。正如文档所说:

- 偏僻的

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

这相当于git pull在每个子模块中运行,这通常正是您想要的。

于 2014-01-17T20:24:01.040 回答
148

在您的项目父目录中,运行:

git submodule update --init

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

git submodule update --init --recursive

有时这仍然不起作用,因为在更新子模块时,不知何故您在本地子模块目录中进行了本地更改。

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

git submodule update --init --recursive
于 2013-06-18T08:25:40.847 回答
82

您的主项目指向子模块应位于的特定提交。git submodule 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 submodule update他们将更新子模块,假设它已经被初始化。

于 2011-04-29T06:12:17.560 回答
26

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

方案 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(快进)。顺便说一句,我已经关闭了rebase)。

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,删除“输入 MODULE_PATH”输出。使用'echo $path'(必须用单引号),子模块的路径被写入输出。

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

最后,提交带有一些消息,说明父存储库已更新。如果什么都不做,默认情况下会忽略此提交。把它推到原点,你就完成了。

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

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

于 2014-01-14T15:46:33.570 回答
20

简单明了,获取子模块:

git submodule update --init --recursive

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

git submodule foreach git pull origin master
于 2017-08-17T20:48:04.337 回答
19

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

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

旧形式是:

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

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

请参阅Nguyễn Thái Ngọc Duy ( ) 的提交 a282f5a(2019 年 4 月 12 日(由Junio C Hamano 合并 -- --提交 f1c9f6c中,2019 年 4 月 25 日)pclouds
gitster

submodule foreach:修复“ <command> --quiet”不被尊重

罗宾报告说

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

不再是真的安静了。在fc1b924 ( : port subcommand ' ' from shell to C, 2018-05-10, Git v2.19.0-rc0)
之前它应该是安静的,因为那时不能不小心吃掉选项。submodulesubmoduleforeachparseopt

" git pull" 表现得好像--quiet没有给出。

发生这种情况是因为parseoptinsubmodule--helper将尝试解析这两个--quiet选项,就好像它们是 foreach 的选项,而不是git-pull's。
已解析的选项将从命令行中删除。所以当我们稍后拉取时,我们只执行这个

git pull origin

调用子模块助手时,--在“”前添加“ git pull”将停止parseopt解析不真正属于的选项 submodule--helper foreach

PARSE_OPT_KEEP_UNKNOWN作为安全措施被删除。parseopt永远不会看到未知的选项或出现问题。在我查看它们时,还有一些使用字符串更新。

在此期间,我还将“ --”添加到其他将“”传递$@submodule--helper. " $@" 在这些情况下是路径,不太可能是 --something-like-this
但重点仍然存在,git-submodule已经解析和分类了什么是选项,什么是路径。
submodule--helper永远不应将经过的路径视为git-submodule选项,即使它们看起来像一个。


并且 Git 2.23(2019 年第三季度)修复了另一个问题:git submodule foreach当使用“”选项时,“”没有保护传递给要在每个子模块中正确运行的命令的命令行选项--recursive

请参阅Morian Sonnet ( ) 的提交 30db18b(2019 年 6 月 24 日(由Junio C Hamano 合并——提交 968eecb中,2019 年 7 月 9 日)momoson
gitster

submodule foreach: 修复选项的递归

来电:

git submodule foreach --recursive <subcommand> --<option>

导致一个错误,指出该选项--<option>submodule--helper.
这当然只是,when<option>不是git submodule foreach.

这样做的原因是,上面的调用在内部被转换为对 submodule--helper 的调用:

git submodule--helper foreach --recursive \
   -- <subcommand> --<option>

此调用首先在第一级子模块中执行带有其选项的子命令,然后通过调用调用的下一次迭代submodule foreach继续

git --super-prefix <submodulepath> submodule--helper \
  foreach --recursive <subcommand> --<option>

在第一级子模块内。请注意,子命令前面的双破折号缺失。

这个问题最近才开始出现,因为在提交a282f5aPARSE_OPT_KEEP_UNKNOWN中删除了参数解析的标志。 因此,现在抱怨未知选项,因为双破折号没有正确结束参​​数解析。git submodule foreach

此提交通过在递归期间在子命令前面添加双破折号来解决问题。


请注意,在 Git 2.29(2020 年第四季度)之前,“ git submodule update --quietman并没有压制底层的“rebase”和“pull”命令。

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

submodule update--quiet: 用“ ”沉默底层合并/变基

签字人:Theodore Dubois

命令如

$ git pull --rebase --recurse-submodules --quiet  

从合并或变基中产生非安静的输出。调用 " " 和 " "时
传递选项。--quietrebasemerge

还修复了( man ) -v的解析。git submodule update

e84c3cf3 (" git-submodule.sh: accept verbose flag in cmd_updateto be non-quiet", 2018-08-14, Git v2.19.0-rc0 -- merge ) 教 " git submodule update" ( man )取 " --quiet" 时,它显然不知道如何${GIT_QUIET: +--quiet} 有效,审阅者似乎错过了将变量设置为“0”,而不是取消设置,仍然导致“ --quiet”被传递给底层命令。

于 2019-04-27T22:50:29.980 回答
8
git pull --recurse-submodules

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

于 2019-01-22T15:03:26.040 回答
7

这对我有用,可以更新到最新的提交

git submodule update --recursive --remote --init

于 2021-05-12T12:57:55.257 回答
4

就我而言,我想git更新到最新版本,同时重新填充任何丢失的文件。

以下恢复了丢失的文件(感谢--force这里似乎没有提到),但它没有拉任何新的提交:

git submodule update --init --recursive --force

这做到了:

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

于 2018-09-26T05:00:30.187 回答
3

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

更新

更新已注册的子模块,即克隆缺少的子模块并签出包含存储库的索引中指定的提交。这将使子模块 HEAD 分离,除非指定 --rebase 或 --merge 或键 submodule.$name.update 设置为 rebase 或 merge。

所以,git submodule update结帐,但它是在包含存储库的索引中的提交。它根本不知道上游的新提交。因此,转到您的子模块,获取您想要的提交并在主存储库中提交更新的子模块状态,然后执行git submodule update.

于 2011-04-29T06:01:10.913 回答
3

如果您不知道主机分支,请执行以下操作:

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

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

于 2019-02-14T13:21:18.157 回答
3

如果您要检查master每个子模块的分支——您可以为此目的使用以下命令:

git submodule foreach git checkout master
于 2020-04-12T19:48:51.290 回答
2

这是一个很棒的单线,可以将所有内容更新到 master 上的最新版本:

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

感谢马克·贾奎斯

于 2017-07-28T04:29:02.440 回答
2

对我来说一切都没有git submodule用。但这有效:

cd <path/to/submodule>
git pull

它下载并更新第三方存储库。然后

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

它会更新您的远程仓库(带有指向最后一次提交的链接repo@xxxxxx)。

于 2021-10-06T08:24:16.373 回答
0

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

--recurse-submodules 

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

git fetch --recurse-submodules

其他

git pull --update --recurse-submodules

ETC...

于 2020-11-18T08:39:21.283 回答