【问题标题】:Update Git submodule to latest commit on origin将 Git 子模块更新为源上的最新提交
【发布时间】:2011-08-15 06:14:20
【问题描述】:

我有一个带有 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 loggit show 否认存在新提交。到目前为止,我只是 rm-ing 模块并重新添加它,但这在原则上是错误的,在实践中也是乏味的。

【问题讨论】:

  • David Z 的回答似乎是这样做的更好方法 - 现在 Git 具有您通过 --remote 选项内置的功能,也许将其标记为已接受的答案而不是有用的比杰森回答中的“手工”方法?
  • 我非常同意@MarkAmery。虽然 Jason 提供了一个可行的解决方案,但这并不是预期的方法,因为它会将子模块的提交指针留在错误的提交标识符处。新的--remote 在这个时间点绝对是一个更好的解决方案,并且由于这个问题已经链接到一个关于子模块的 Github Gist,我觉得新的读者看到新的答案会更好。
  • 使用hunter2 密码很不错:o)

标签: git git-submodules git-pull


【解决方案1】:

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

【讨论】:

  • git submodule foreach git pull
  • @Nicklas 在这种情况下,使用git submodule foreach git pull origin master
  • 在这一点上,有了所有这些更正的更正,我需要有人写一篇解释性的博客文章并指出我那里。请。
  • 对“foreach”方法的小幅改进——如果子模块中有子模块,您可能需要在其中添加--recursive。所以:git submodule foreach --recursive git pull origin master.
  • @Abdull -a 开关git commit "告诉[s]命令自动暂存已修改和删除的文件,但您没有告诉Git的新文件不受影响。 "
【解决方案2】:

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

git submodule update --remote --merge

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

--远程

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

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

【讨论】:

  • “相当于在每个子模块中运行git pull澄清一下,你的回答和git submodule foreach git pull之间没有区别(从用户的角度来看)?
  • @Dennis 它基本上做同样的事情,但我不确定功能是否完全相同。可能有一些我不知道的细微差异,例如以这两个命令响应某些配置设置的方式。
  • 我希望我能投票给这 10,000X。为什么这在 git 的文档中没有显示在任何地方?巨大的疏忽。
  • 对我来说,它们实际上差别很大; foreach git pull 只检查了它们,但没有更新主仓库的指针以指向子模块的较新提交。只有 --remote 它指向最新的提交。
  • 为什么是 --merge 选项?它有什么不同?
【解决方案3】:

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

git submodule update --init

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

git submodule update --init --recursive

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

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

git submodule update --init --recursive

【讨论】:

  • 这是真正的答案。我可以以某种方式将它推送到我的远程存储库吗?
  • 这适用于新的子模块!我可以更新所有其他的,但新子模块的文件夹将保持空,直到我运行这个命令。
  • 它不会对现有子模块进行更改
  • 这将克隆子模块,但仅限于主仓库中指定的提交。你需要 cd 进入子模块文件夹并运行 git pull origin <branch_name> 以获得最新的提交,运行 git submodule update --init
【解决方案4】:

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

【讨论】:

    【解决方案5】:

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

    场景 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 作业中运行此脚本,该脚本随后链接到预定的自动部署,它就像一个魅力。

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

    【讨论】:

    • !@#$% SO 我们正在使用类似于您的脚本;注意:我们在 for 循环中使用 ``` git submodule foreach --recursive --quiet pwd ``` 而不是 ``` git submodule foreach --quiet 'echo $path' ```。 pwd 命令为每个存在的子模块打印正确的“绝对路径”; --recursive 确保我们访问 所有 子模块,包括可能存在于大型项目中的 submodules-within-submodules-...。这两种方法都会导致包含空格的目录出现问题,例如/c/Users/Ger/Project\ Files/... 因此政策是从不在我们项目的任何地方使用空格。
    • 这很好,你是对的,在某些答案中甚至存在关于问题的误解,但正如大卫 Z 的出色回答所指出的那样,你的脚本是不必要的,因为功能已经自 2013 年年中他们添​​加了 --remote 选项以来,它内置于 Git 中。 git submodule update --remote 的行为与您的脚本大致相同。
    • @GerHobbelt 谢谢。你是对的,我们只有 1 级子模块,所以我从没想过让它递归。在我有机会验证它是否按预期工作之前,我不会更新脚本,但我的脚本肯定会包含子子模块。至于文件夹中的空格,这听起来绝对是要避免的! :S
    • @MarkAmery 感谢您的反馈。但是,我看到了 1 个问题:不能为子模块指定分支。来自 git 手册:The remote branch used defaults to master, but the branch name may be overridden by setting the submodule.<name>.branch option in either .gitmodules or .git/config (with .git/config taking precedence). 每次我想对另一个分支而不是 master 执行此操作时,我都不想编辑 .gitmodules 或 .git/config 。但也许我错过了什么?此外,该方法似乎强制执行递归合并(因此错过了快进的可能性)。
    • 最后一件事:我尝试了@DavidZ 的方法,但它似乎没有做确切的事情,我开始做(以及哪个操作正在询问):将子模块的 HEAD 提交添加到父(即“更新指针”)。然而,它似乎在获取和合并所有子模块中的最新更改方面做得非常好(而且速度更快)。唉,默认情况下仅来自 master 分支(除非您编辑 .gitmodules 文件(见上文))。
    【解决方案6】:

    简单明了,获取子模块:

    git submodule update --init --recursive
    

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

    git submodule foreach git pull origin master
    

    【讨论】:

      【解决方案7】:

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

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

      旧形式是:

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

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

      参见Nguyễn Thái Ngọc Duy (pclouds)commit a282f5a(2019 年 4 月 12 日)。
      (由 Junio C Hamano -- gitster -- 合并于 commit f1c9f6c,2019 年 4 月 25 日)

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

      罗宾报告说

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

      真的不再安静了。
      fc1b924之前应该是安静的(submodule: port submodule subcommand 'foreach' from shell to C, 2018-05-10, Git v2.19.0-rc0)因为parseopt不能意外吃然后选择。

      git pull”的行为就像没有给出--quiet一样。

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

      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 年第三季度)修复了另一个问题:当使用“--recursive”选项时,“git submodule foreach”没有保护传递给在每个子模块中正确运行的命令的命令行选项。

      参见Morian Sonnet (momoson)commit 30db18b(2019 年 6 月 24 日)。
      (由 Junio C Hamano -- gitster -- 合并于 commit 968eecb,2019 年 7 月 9 日)

      submodule foreach:修复选项递归

      调用:

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

      导致一个错误,指出选项 --&lt;option&gt; 未知 submodule--helper.
      这当然只是当&lt;option&gt; 不是git submodule foreach 的有效选项时。

      这样做的原因是,上面的调用在内部被翻译成 调用子模块--helper:

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

      此调用首先执行带有选项的子命令 第一级子模块并继续调用下一次迭代 submodule foreach 电话

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

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

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

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


      请注意,在 Git 2.29(2020 年第 4 季度)之前,“git submodule update --quiet(man) 不会抑制底层的“rebase”和“pull”命令。

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

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

      签字人:Theodore Dubois

      命令如

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

      从合并或变基中产生非安静的输出。
      调用“rebase”和“merge”时传递--quiet 选项。

      同时修复git submodule update的解析(man) -v.

      e84c3cf3(“git-submodule.sh:接受cmd_update中的详细标志为非安静”,2018-08-14,Git v2.19.0-rc0 -- merge)教导“git submodule update(man) 取“--quiet”,它显然不知道${GIT_QUIET:+--quiet} 是如何工作的,而且审稿人似乎错过了将变量设置为“ 0”,而不是取消设置,仍然会导致“--quiet”被传递给底层命令。

      【讨论】:

        【解决方案8】:
        git pull --recurse-submodules
        

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

        【讨论】:

          【解决方案9】:

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

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

          【讨论】:

          • 这个问题已经有很多相似但不完全相同的答案。如果您能解释一下您的方法如何改进这里已经说过的内容,将会有所帮助。
          【解决方案10】:

          就我而言,我希望git 更新到最新版本,同时重新填充所有丢失的文件。

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

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

          这样做了:

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

          【讨论】:

            【解决方案11】:

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

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

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

            【讨论】:

              【解决方案12】:

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

              更新

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

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

              【讨论】:

              • 似乎如果我将子模块移动到不同的提交,然后运行git submodule update,更新会将子模块移动到超级项目的当前 HEAD 中指定的提交。 (无论超级项目中的最新提交说子项目应该是什么——这种行为,在 Jason 的帖子中解释之后,对我来说似乎是合乎逻辑的)它似乎也可以获取,但仅在子项目提交错误的情况下,这增加了我的困惑。
              【解决方案13】:

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

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

              Thanks to Mark Jaquith

              【讨论】:

                【解决方案14】:

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

                git submodule foreach git checkout master
                

                【讨论】:

                  【解决方案15】:

                  对我来说,所有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)。

                  【讨论】:

                    【解决方案16】:

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

                    --recurse-submodules 
                    

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

                    git fetch --recurse-submodules
                    

                    另一个

                    git pull --update --recurse-submodules
                    

                    等等……

                    【讨论】:

                      猜你喜欢
                      • 2012-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2021-10-15
                      • 2021-05-06
                      • 2013-01-03
                      • 2016-02-01
                      • 2017-04-11
                      • 2020-08-09
                      相关资源
                      最近更新 更多