将Git子模块更新为最新的原始提交

Tha*_*tos 799 git git-pull git-submodules

我有一个带有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
$ ...
Run Code Online (Sandbox Code Playgroud)

我也试过git fetch mod,这似乎做取(但不能可能,因为它不要求输入密码!),但git loggit show否认新提交的存在.到目前为止,我刚刚rm对模块进行了重新添加,但这在原则上是错误的,在实践中也是乏味的.

Jas*_*son 1372

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"
Run Code Online (Sandbox Code Playgroud)

或者,如果你是一个忙碌的人:

git submodule foreach git pull origin master
Run Code Online (Sandbox Code Playgroud)

  • `git submodule foreach git pull` (312认同)
  • @Nicklas在这种情况下,使用`git submodule foreach git pull origin master`. (84认同)
  • 在这一点上,通过对更正的所有这些更正,我需要有人写一个解释性博客文章并指出我.请. (52认同)
  • 对'foreach'方法的微小改进 - 如果你在子模块中有子模块,你可能想在那里添加--recursive.所以:`git submodule foreach --recursive git pull origin master`. (24认同)
  • @Abdull`-a`开关用于`git commit`"告诉[s]命令自动暂存已经修改和删除的文件,但是你没有告诉Git的新文件不受影响." (4认同)
  • @MathiasBynens:您必须检查子模块上的分支,因为它默认处于分离状态. (3认同)
  • 我认为解决方案是正确的,但解释`git submodule update`的做法是错误的.在初始化的子模块上,该命令将检查父级"记住"的子模块的提交. (3认同)
  • 如果每个 git 子模块都有不同的默认分支怎么办? (2认同)
  • git commit ...之前缺少git add ... (2认同)
  • `git submodule update --remote` 将使用各自的远程主控更新所有子模块的本地主控 (2认同)
  • 我建议使用 `cd -` 而不是 `cd ..` 来确保你回到根项目而不是父文件夹中。 (2认同)
  • 注意:由于最近默认分支名称发生更改(master -> main),您需要相应地更新命令。 (2认同)

Dav*_*d Z 424

Git 1.8.2提供了一个新选项--remote,可以实现这种行为.运行

git submodule update --remote --merge
Run Code Online (Sandbox Code Playgroud)

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

- 远程

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

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

  • 现在,使用“master”或“main”分支混合存储库的“git submodule foreach git pull origin master”将失败。因此 `git submodule update --remote` 是更好的解决方案。 (13认同)
  • *"相当于在每个子模块中运行`git pull`"*为了澄清,你的答案与`git submodule foreach git pull`之间没有区别(从用户的角度来看)? (3认同)
  • 我希望我能投票支持这 10,000 倍。为什么在任何地方的 git 文档中都没有显示?巨大的疏忽。 (3认同)
  • 对我来说,他们实际上差别很大; `foreach git pull`只检查了它们,但没有更新主repo的指针指向子模块的较新提交.只有`--remote`它才能指向最新的提交. (3认同)
  • 为什么--merge选项?它有什么不同? (3认同)
  • @Dennis它的功能基本相同,但我不确定功能是否与_exactly_相同.可能存在一些我不知道的细微差别,例如,两个命令响应某些配置设置的方式. (2认同)
  • 如果它相当于 `git pull`,那么 `--merge` 不是默认只使用 `--remote` 吗? (2认同)
  • 使用它与`-j` 并行更新子模块ex。'-j8' 最多可创建 8 个更新作业。 (2认同)

pin*_*nux 115

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

git submodule update --init
Run Code Online (Sandbox Code Playgroud)

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

git submodule update --init --recursive
Run Code Online (Sandbox Code Playgroud)

有时这仍然无法正常工作,因为在更新子模块时,您在本地子模块目录中进行了本地更改.

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

git submodule update --init --recursive
Run Code Online (Sandbox Code Playgroud)

  • 这是真正的答案.我能以某种方式将它推送到我的远程存储库吗? (5认同)
  • 这将克隆子模块,但仅限于主存储库中指定的提交。在运行“git submodule update --init”后,您需要“cd”进入子模块文件夹并运行“git pull origin <branch_name>”以获取最新提交 (3认同)
  • 它不会对现有子模块进行更改 (2认同)

Mar*_*air 73

您的主项目指向子模块应该处于的特定提交.git submodule update尝试在每个已初始化的子模块中检出该提交是什么.子模块实际上是一个独立的存储库 - 只需在子模块中创建一个新的提交并推送它是不够的,您还需要在主项目中显式添加新版本的子模块.

因此,在您的情况下,您应该在子模块中找到正确的提交 - 让我们假设这是主要的提示:

cd mod
git checkout master
git pull origin master
Run Code Online (Sandbox Code Playgroud)

现在回到主项目,暂存子模块并提交:

cd ..
git add mod
git commit -m "Updating the submodule 'mod' to the latest version"
Run Code Online (Sandbox Code Playgroud)

现在推送您的新版主项目:

git push origin master
Run Code Online (Sandbox Code Playgroud)

从这一点开始,如果其他人更新了他们的主项目,那么master他们将更新子模块,假设它已被初始化.


Fre*_*ing 22

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

场景1

使用我的父repo指向子模块的指针,我想检查父repo指向的每个子模块中的提交,可能是在首次迭代所有子模块并从远程更新/拉取这些子模块之后.

正如所指出的那样,这是完成的

git submodule foreach git pull origin BRANCH
git submodule update
Run Code Online (Sandbox Code Playgroud)

场景2,我认为是OP的目标

在1个或多个子模块中发生了新的事情,我想1)拉出这些变化,2)更新父repo以指向这个/这些子模块的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
Run Code Online (Sandbox Code Playgroud)

不太实用,因为你必须硬编码所有n个子模块的n个路径,例如脚本来更新父repo的提交指针.

通过每个子模块进行自动迭代,更新父repo指针(使用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
Run Code Online (Sandbox Code Playgroud)

要运行它,请执行

git-update-submodules.sh /path/to/base/repo BRANCH_NAME
Run Code Online (Sandbox Code Playgroud)

首先,我假设所有repo上都存在名为$ BRANCH(第二个参数)的分支.随意使这更复杂.

前几节是检查参数是否存在的一些部分.然后我拉出父回购的最新东西(我更喜欢使用--ff(快进)每当我做拉动时.我已经关闭了,顺便说一句).

git checkout $BRANCH && git pull --ff origin $BRANCH
Run Code Online (Sandbox Code Playgroud)

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

git submodule sync
git submodule init
git submodule update
Run Code Online (Sandbox Code Playgroud)

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

git submodule foreach "(git checkout $BRANCH && git pull --ff origin $BRANCH && git push origin $BRANCH) || true"
Run Code Online (Sandbox Code Playgroud)

注意一些事情:首先,我正在使用链接一些git命令git add- 意味着前一个命令必须执行w/o错误.

在可能成功拉动之后(如果在遥控器上发现了新的东西),我做了一个推动,以确保在客户端上不会留下可能的合并提交.同样,只有发生如果拉新的东西实际上带来的.

最后,最后一步&&是确保脚本继续出错.为了使这项工作,迭代中的所有内容必须用双引号括起来,并且git-commands包含在parantheses(operator precedence)中.

我最喜欢的部分:

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

迭代所有子模块 - 使用|| true,删除'Entering MODULE_PATH'输出.使用--quiet(必须使用单引号),子模块的路径将写入输出.

这个相对子模块路径列表在一个数组('echo $path')中捕获- 最后迭代这个并做$(...)更新父存储库.

最后,提交一些消息,解释父代表已更新.如果没有做任何事情,默认情况下将忽略此提交.把它推到原点,你就完成了.

我有一个在jenkins工作中运行它的脚本,之后链接到计划的自动部署,它就像一个魅力.

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

  • !@#$%因此,我们正在使用与您相似的脚本;一个注意事项:我们在for循环内使用git子模块foreach --recursive --quiet pwd而不是git子模块foreach --quiet'echo $ path'。pwd命令为每个存在的子模块打印正确的“绝对路径”。--recursive确保我们访问*所有*子模块,包括可能存在于大型项目中的submodules-within-submodules-...。这两种方法都会导致目录包含空格的麻烦,例如`/ c / Users / Ger / Project \ Files / ...`,因此策略是*绝不要在项目的任何地方使用空格。 (2认同)
  • 很好,您是对的,甚至对问题的答案有些误解,但是正如David Z的出色答案所指出的那样,您的脚本是不必要的,因为该功能自2013年中期以来已内置到Git中他们添加了--remote选项。git submodule update --remote的行为近似于脚本的行为。 (2认同)

Dan*_*ncă 18

简单明了,要获取子模块:

git submodule update --init --recursive
Run Code Online (Sandbox Code Playgroud)

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

git submodule foreach git pull origin master
Run Code Online (Sandbox Code Playgroud)


Gab*_*les 12

如何更新存储库中的所有 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
#
# For just Option 2 above: now add and commit these subrepo changes 
# you just pulled
git add -A
git commit -m "Update all subrepos to their latest upstream changes"
Run Code Online (Sandbox Code Playgroud)

细节

  1. 选项 1:作为外部存储库的用户,尝试使所有子模块进入外部存储库开发人员预期的状态:
    git submodule update --init --recursive
    
    Run Code Online (Sandbox Code Playgroud)
  2. 选项 2:作为外部存储库的开发人员,尝试将所有子模块更新到推送到每个远程存储库的默认分支的最新提交(即:将所有子存储库更新到每个子存储库开发人员预期的最新状态):
    git submodule update --init --recursive --remote
    
    Run Code Online (Sandbox Code Playgroud) ...代替使用git submodule foreach --recursive git pull origin mastergit submodule foreach --recursive git pull origin main

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

上面使用的选项的解释:

  • 上面的部分--init会初始化子模块,以防您刚刚克隆了存储库但尚未完成此操作
  • --recursive对子模块内的子模块执行此操作,永远递归下去
  • --remote表示将子模块更新到子模块的默认远程默认分支上的最新提交。例如,在大多数情况下,这就像对每个子模块执行git pull origin masteror操作。git pull origin main如果您想更新到最外层存储库(超级存储库)指定的提交,请不要​​这样做--remote

git submodule foreach --recursive git pull(不要使用这个——它经常失败)vs git 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 upstream develop例如,您可能需要。或者,更糟糕的是,可能没有任何 git submodule foreach命令适用于您的外部存储库,因为每个子模块可能需要不同的命令来从其默认远程和默认分支更新自身。然而,在所有情况下,我发现这确实有效,包括对于您可能使用我上面刚刚介绍的几个命令之一的所有情况git submodule foreach。所以,用这个代替:

git submodule update --recursive --remote
Run Code Online (Sandbox Code Playgroud)

无论如何,以下是我在这个答案下对此的几点评论:

(1/4) @DavidZ,很多人认为git submodule foreach git pullgit submodule update --remote是同一件事,后者只是更新的命令。然而,它们不是同一件事。在多种情况下git submodule foreach git pull都会失败,但效果很好!如果您的子模块指向没有分支指向它的提交哈希(在现实开发中经常出现这种情况,您需要为外部存储库提供特定版本的子模块),那么该子模块...git submodule update --remote

(2/4)...处于分离的 HEAD 状态。在这种情况下,git submodule foreach git pull无法在该子模块上运行git pull,因为分离的 HEAD 不能有上游分支。git submodule update --remote但是,效果很好!它似乎调用git pull origin main该子模块 iforigin是默认远程并且main是该默认远程上的默认分支,或者git pull origin master,例如, iforigin是默认远程但是master默认分支。

(3/4) 此外,在许多工作正常的git submodule foreach git pull origin master情况下甚至会失败,因为许多子模块用作默认分支,并且许多其他子模块用作默认分支,因为 GitHub 从 更改为最近为了摆脱相关术语美国的奴隶制(“主人”和“奴隶”)。git submodule update --remotemastermainmastermain

(4/4) 所以,我添加了显式的远程和分支,以更清楚地表明它们是经常需要的,并提醒人们,这git pull经常是不够的,并且git pull origin master可能不起作用,并且git pull origin main当前者不起作用时可能会起作用,但也可能不起作用,并且它们本身都不与 相同git submodule update --remote,因为后一个命令足够智能,可以git pull <default_remote> <default_branch>为每个子模块执行操作,显然根据每个子模块的需要调整远程和分支。

相关及其他研究

  1. 我的一般git submodule说明:https://github.com/ElectricRCAircraftGuy/eRCaGuy_dotfiles#git-submodules-and-git-lfs-how-to-clone-this-repo-and-all-git-submodules-and-git-lfs-文件
  2. 如何查找存储库的主分支:/sf/answers/3456899841/
  3. 如何通过在其中运行自定义命令来更新每个子存储库git submodule foreach <cmd>/sf/answers/3202130781/
  4. man git submodule- 然后搜索foreach--remote等。
  5. 我对如何解决与 git 子模块的冲突的回答,在包含它们的外部存储库中


Job*_*mes 9

这对我来说可以更新到最新的提交

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

  • 这个问题已经有很多相似但不相同的答案。如果您能解释一下您的方法如何改进此处已经说过的内容,将会有所帮助。 (4认同)

小智 8

git pull --recurse-submodules
Run Code Online (Sandbox Code Playgroud)

这将拉取所有最新提交。


Von*_*onC 6

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

git submodule update --recursive --remote --merge --force
Run Code Online (Sandbox Code Playgroud)

较旧的形式是:

git submodule foreach --quiet git pull --quiet origin
Run Code Online (Sandbox Code Playgroud)

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

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)

submodule foreach: fix "<command> --quiet" not being respected

Robin reported that

git submodule foreach --quiet git pull --quiet origin
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

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.


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)

submodule foreach: fix recursion of options

Calling:

git submodule foreach --recursive <subcommand> --<option>
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

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.