我们正在使用git子模块来管理几个大型项目,这些项目依赖于我们开发的许多其他库。每个库都是作为子模块带入相关项目的单独的存储库。在开发过程中,我们通常只想获取每个相关子模块的最新版本。

git是否有内置命令来执行此操作?如果不是,那么Windows批处理文件或可以执行此操作的类似文件如何?

评论

git-deep应该可以帮助解决这个问题。

@Brad是否要将子模块的副本更新为主项目中命名的commit revs?还是要从每个子模块中提取最新的HEAD提交?这里的大多数答案都针对前者。很多人想要后者。

#1 楼

如果这是您第一次签出仓库,则需要先使用--init

git submodule update --init --recursive


对于git 1.8.2或更高版本,已添加选项--remote以支持更新到远程分支的最新提示:

git submodule update --recursive --remote


这具有遵守.gitmodules.git/config文件中指定的任何“非默认”分支的附加好处(如果您碰巧遇到了有任何默认值是origin / master,在这种情况下,这里的其他答案也可以使用)。

对于git 1.7.3或更高版本,您可以使用(但下面有关更新的问题)仍然适用):

git submodule update --recursive


或:

git pull --recurse-submodules


如果要将子模块拉到最新提交,而不是当前的repo指向。

有关详细信息,请参见git-submodule(1)

评论


如今,您可能应该使用git子模块更新--recursive。

–詹斯·科尔(Jens Kohl)
2011年9月30日14:12在

性能改进:git子模块foreach“(git checkout master; git pull)&”

– Bogdan Gusiev
2011年11月7日13:27



更新会将每个子模块更新为指定的修订版,而不是将其更新为该存储库的最新版本。

– Peter DeWeese
2012年12月18日在20:56

只需添加一下,如果您的某些子模块跟踪该特定子模块的不同分支或位置名称,则在该命令末尾盲目粘贴原始主机可能会产生意外结果。对某些人很明显,但可能对每个人都不是。

–内森·霍恩比(Nathan Hornby)
2014年7月23日18:00



只是为了向大家澄清。 git submodule update --recursive查找父存储库为每个子模块存储的修订,然后在每个子模块中检出该修订。它不会为每个子模块提取最新的提交。 git submodule foreach git pull origin master或git pull origin master --recurse-submodules是您想要的,如果您打算将每个子模块从其原始存储库更新到最新版本。只有这样,您才能在父存储库中获得未决的更改,并带有子模块的更新的修订哈希。检查那些就可以了。

–雪佛兰
15-10-22在16:14



#2 楼

git pull --recurse-submodules --jobs=10



git在1.8.5中首先学习的功能。


直到该错误被修复之前,您第一次需要运行


git子模块更新--init --recursive


评论


赞成,我用这个:别名update_submodules ='git pull --recurse-submodules && git submodule update'

– Stephen C
2011年12月6日在22:41



如果子模块至少已被拉出一次,但对于从未检出的子模块,则此方法有效,请参见下面的gahooa答案。

–马特·布朗(Matt Browne)
13年1月24日在4:11

这将拉到最高仓库指定的版本;它不会拉头。例如,如果TopRepo在SubRepo的HEAD后面指定了版本2,则这会将SubRepo拉到落后2的版本。这里的其他答案在SubRepo中拉HEAD。

–克里斯·莫斯基尼(Chris Moschini)
2014年6月12日17:09

请注意,无论git pull --recurse-submodules还是git submodule update --recursive都不会初始化新添加的子模块。要初始化它们,您需要运行git子模块更新--recursive --init。从手册中引用:如果子模块尚未初始化,而您只想使用存储在.gitmodules中的设置,则可以使用--init选项自动初始化子模块。

–patryk.beza
16-3-2在22:59



可能会向git submodule update --recursive --remote添加提示,这还会将子模块更新为远程最新修订版,而不是已存储的SHA-1。

– Hanno S.
16年6月17日在10:59

#3 楼

在init上运行以下命令:

git submodule update --init --recursive


从git repo目录中,最适合我。

这将拉动所有最新的子模块。

说明

git - the base command to perform any git command
    submodule - Inspects, updates and manages submodules.
        update - Update the registered submodules to match what the superproject
        expects by cloning missing submodules and updating the working tree of the
        submodules. The "updating" can be done in several ways depending on command
        line options and the value of submodule.<name>.update configuration variable.
            --init without the explicit init step if you do not intend to customize
            any submodule locations.
            --recursive is specified, this command will recurse into the registered
            submodules, and update any nested submodules within.



之后,您可以运行:

git submodule update --recursive


可以从git repo目录中找到最适合我的计算机。

这将提取所有最新的子模块。

评论


是的-投票率最高的答案是'09最佳解决方案,但这绝对是现在更简单,更直观的方法。

–Michael Scott Cuthbert
15年8月28日在17:52

@MichaelScottCuthbert谢谢,我敢肯定,再过三年,这个命令也会很疯狂

–abc123
15年8月28日在19:28

但是,这不会从子模块中检出最新修订,而只会签出父级正在跟踪的最新修订。

–内森·奥斯曼(Nathan Osman)
16-4-5在4:44



@NathanOsman就是您想要的...如果不遵循父母的修订版本跟踪,您将得到残破的代码。如果您是父母的维护者,则可以自己更新并提交。

–abc123
16年4月6日在19:39

是的,但据我了解,这不是OP想要的。

–内森·奥斯曼(Nathan Osman)
16-4-6的23:31

#4 楼

注意:这是从2009年开始的,那时可能还不错,但是现在有更好的选择。

我们使用它。它叫做git-pup

#!/bin/bash
# Exists to fully update the git repo that you are sitting in...

git pull && git submodule init && git submodule update && git submodule status


只需将其放在合适的bin目录(/ usr / local / bin)中。如果在Windows上,则可能需要修改语法才能使其正常工作:)

更新:

以响应原始作者关于拉入所有内容的评论。所有子模块的HEADs-这是一个好问题。

我很确定git内部没有用于此的命令。为此,您需要确定子模块的真正含义是什么。这可能很简单,例如说master是最新的分支,等等...

之后,创建一个执行以下操作的简单脚本: />检查git submodule status是否有“已修改”的存储库。输出行的第一个字符表明了这一点。如果子仓库已被修改,则您可能不想继续。
对于列出的每个仓库,将cd放入其目录并运行git checkout master && git pull。检查错误。
最后,我建议您向用户打印显示以指示子模块的当前状态-也许提示他们添加所有子模块并提交?

我d要提及,这种样式并不是git子模块真正设计的目的。通常,您想说“ LibraryX”的版本为“ 2.32”,并且会一直保持这种状态,直到我告诉它“升级”为止。

,从某种意义上讲,您正在使用描述的脚本,但只是更加自动。需要小心!

更新2:

如果您使用的是Windows平台,则可能要考虑使用Python来实现脚本,因为它在这些平台中非常强大地区。如果您使用的是Unix / Linux,那么我建议您只使用bash脚本。

需要任何说明吗?只需发表评论即可。

评论


我认为那不是我想要的。不会拉出上次提交超级项目的子模块的版本。我想拉所有子模块的头版。

–布拉德·罗宾逊(Brad Robinson)
09年6月23日在2:30

这很好用,不仅可以更新子模块,而且还可以在需要时第一次获取它们。

–马特·布朗(Matt Browne)
13年1月24日在4:12

我刚得到“当前分支没有跟踪信息。请指定要合并的分支。”无论我尝试什么:/

–内森·霍恩比(Nathan Hornby)
2014年8月4日在9:45

为什么不为其创建别名? git config --global alias.pup'!git pull && git子模块init && git子模块更新&& git子模块状态',然后将其用作git pup,无需任何脚本。

– Fracz
16年2月11日在21:40

谢谢,由于某些原因,即使我拥有git 1.9.1,我也必须在第一次拉入包含子模块后必须执行git子模块init,以便一切都能正常工作。

–本·乌斯曼(Ben Usman)
16年5月12日在21:25

#5 楼

亨里克走在正确的轨道上。 “ foreach”命令可以执行任何任意的shell脚本。可能有两个选择可以拉动最新的消息,分别是


所有初始化的子模块并运行给定命令。

#6 楼

以下内容在Windows上对我有用。

git submodule init
git submodule update


评论


这显然不是OP所要求的。它只会更新到关联的子模块提交,而不是最新的。

–帕特里克(Patrick)
2011-10-29 4:49

然而,这是此页面上唯一让git在我第一次签出回购协议时提取子模块的东西

–扫帚的头
2013年1月11日15:34

也可以使用:git submodule update --init --recursive(特别是如果所涉及的子模块是来自新克隆的RestKit)

– HCdev
2013年9月30日4:28



#7 楼

可能会发生子模块的默认分支不是master的情况,这就是我自动执行完整Git子模块升级的方式:

git submodule init
git submodule update
git submodule foreach 'git fetch origin; git checkout $(git rev-parse --abbrev-ref HEAD); git reset --hard origin/$(git rev-parse --abbrev-ref HEAD); git submodule update --recursive; git clean -dfx'


评论


在许多问题的许多答案中,这一个对我有用(2019,带有特定哈希ID的github错误)

– philshem
19-10-25在7:07

#8 楼

编辑:

在评论中(由philfreo指出),需要最新版本。如果有任何嵌套子模块需要为最新版本:

git submodule foreach --recursive git pull


-----下面的过时注释-----

这不是官方的方法吗?

git submodule update --init


我每次都使用它。到目前为止没有问题。

编辑:

我刚刚发现可以使用:

git submodule foreach --recursive git submodule update --init 


还递归拉所有子模块,即依赖关系。

评论


您的答案没有回答OP的问题,但是要执行您提出的建议,您可以说git submodule update --init --recursive

– philfreo
2011年4月11日19:30



我知道,需要最新版本。好吧,如果有嵌套的子模块,这可能会很有用:git子模块foreach-递归git pull

–抗毒
2011年4月12日11:14



我无法真正下载这些文件,但是“ git子模块更新--init --recursive”对我有用。

– BrainSlugs83
13年8月28日在6:18

#9 楼

第一次

克隆和初始化子模块

git clone git@github.com:speedovation/kiwi-resources.git resources
git submodule init


Rest


在开发过程中只需拉并更新子模块

git pull --recurse-submodules  && git submodule update --recursive


将Git子模块更新为对起源的最新提交

git submodule foreach git pull origin master


首选方式应在以下

/>
git submodule update --remote --merge


注意:最后两个命令具有相同的行为

评论


我做了一个git clone,没有错误地没有子模块,所有其他选项都没有用,没有人克隆子模块。使用您的git子模块更新可以解决问题。现在,我正在下载克隆第一步中缺少的子模块数据。谢谢。我不擅长git:C

– m3nda
16年1月21日在11:23

这个分析器实际上是一个很好的答案,可以在上面提出一个问题:为什么我必须先“ .. --recursive-submodules ..”,然后再进行“ ... update ...”甚至“ .. .foreach ...”稍后获取最新提交?所有这些看起来都不像GIT! “更新”在做什么,为什么我必须手动进入每个模块才能拉动?这不是“ ... --recurse-submodules ..”在做什么吗?有什么提示吗?

– Peter Branforn
16年6月7日在6:53



#10 楼

我不知道从哪个版本的git开始工作,但这就是您要搜索的内容:

git submodule update --recursive


我将它与git pull一起使用来更新根存储库,也:

git pull && git submodule update --recursive


#11 楼

上面的答案很好,但是我们使用git-hooks使其更容易,但是事实证明,在git 2.14中,可以将git config submodule.recurse设置为true,以使子模块在拉到git存储库时得以更新。

这会带来副作用,即如果所有子模块都在分支上,则将所有子模块都更改为已更改,但是如果您已经需要这种行为,则可以完成此操作。

可以完成通过使用:

git config submodule.recurse true


评论


一定喜欢这个选项,不幸的是,即使您的子模块尚未初始化,仍然需要手动使用git子模块init。

–球团
2月12日,0:27

#12 楼

适用于Windows的Git 2.6.3:

git submodule update --rebase --remote

评论


那是唯一为我工作的人。我什至无法初始化或更新,因为子模块指针指向的版本不再位于远程

– Pavel P
18年7月1日,0:32

#13 楼

从存储库的最高层开始:

git submodule foreach git checkout develop
git submodule foreach git pull


这将切换所有分支以开发并提取最新版本

评论


您是否有类似Everything sln文件的内容,该文件在树中添加了所有项目引用?您还会看到什么错误?你也可以检查你的gitignore文件吗

– Srayan Guhathakurta
19年3月21日在23:13

git子模块foreach git pull原始主机必须附加我要提取的分支。除此之外,效果很好。

–特克斯
19-10-1在10:19

#14 楼

我是通过修改上面的gahooa答案来做到这一点的:

将它与git [alias]集成在一起...

如果您的父项目在.gitmodules中具有以下内容:

[submodule "opt/submodules/solarized"]
    path = opt/submodules/solarized
    url = git@github.com:altercation/solarized.git
[submodule "opt/submodules/intellij-colors-solarized"]
    path = opt/submodules/intellij-colors-solarized
    url = git@github.com:jkaving/intellij-colors-solarized.git


在.gitconfig中添加类似的内容

[alias]
    updatesubs = "!sh -c \"git submodule init && git submodule update && git submodule status\" "


然后更新子模块,运行:

git updatesubs


我的环境设置存储库中有一个示例。

#15 楼

现在您只需要一个简单的git checkout

,只需确保通过以下全局配置启用它即可:git config --global submodule.recurse true

#16 楼

这是从所有git存储库中提取所有命令行的命令行,无论它们是否是子模块:

ROOT=$(git rev-parse --show-toplevel 2> /dev/null)
find "$ROOT" -name .git -type d -execdir git pull -v ';'


如果您在顶级git存储库中运行它,可以将"$ROOT"替换为.

#17 楼

我经常使用此命令,到目前为止,它仍然有效。
git pull
git submodule foreach --recursive git checkout master
git submodule foreach --recursive git pull

希望更快。

#18 楼

我认为您必须编写脚本才能执行此操作。老实说,我可能会安装python来做到这一点,以便您可以对每个目录使用os.walkcd并发出适当的命令。使用python或除批处理之外的其他脚本语言,可以轻松添加/删除子项目,而无需修改脚本。

#19 楼

备注:不太容易,但可行,并且它有其独特的优点。

如果要仅克隆存储库的HEAD版本,并且仅克隆其所有子模块的HEAD(即,检出“ “ trunk”),则可以使用以下Lua脚本。有时,简单的命令git submodule update --init --recursive --remote --no-fetch --depth=1可能会导致无法恢复的git错误。在这种情况下,需要清理.git/modules目录的子目录并使用git clone --separate-git-dir命令手动克隆子模块。唯一的复杂性是在超级项目树中找出URL,子模块.git目录的路径和子模块的路径。它的特点:托管在同一主机上的所有子模块和https://github.com/boostorg/boost.git仅包含相对URL。

-- mkdir boost ; cd boost ; lua ../git-submodules-clone-HEAD.lua https://github.com/boostorg/boost.git .
local module_url = arg[1] or 'https://github.com/boostorg/boost.git'
local module = arg[2] or module_url:match('.+/([_%d%a]+)%.git')
local branch = arg[3] or 'master'
function execute(command)
    print('# ' .. command)
    return os.execute(command)
end
-- execute('rm -rf ' .. module)
if not execute('git clone --single-branch --branch master --depth=1 ' .. module_url .. ' ' .. module) then
    io.stderr:write('can\'t clone repository from ' .. module_url .. ' to ' .. module .. '\n')
    return 1
end
-- cd $module ; git submodule update --init --recursive --remote --no-fetch --depth=1
execute('mkdir -p ' .. module .. '/.git/modules')
assert(io.input(module .. '/.gitmodules'))
local lines = {}
for line in io.lines() do
    table.insert(lines, line)
end
local submodule
local path
local submodule_url
for _, line in ipairs(lines) do
    local submodule_ = line:match('^%[submodule %"([_%d%a]-)%"%]$')
    if submodule_ then
        submodule = submodule_
        path = nil
        submodule_url = nil
    else
        local path_ = line:match('^%s*path = (.+)$')
        if path_ then
            path = path_
        else
            submodule_url = line:match('^%s*url = (.+)$')
        end
        if submodule and path and submodule_url then
            -- execute('rm -rf ' .. path)
            local git_dir = module .. '/.git/modules/' .. path:match('^.-/(.+)$')
            -- execute('rm -rf ' .. git_dir)
            execute('mkdir -p $(dirname "' .. git_dir .. '")')
            if not execute('git clone --depth=1 --single-branch --branch=' .. branch .. ' --separate-git-dir ' .. git_dir .. ' ' .. module_url .. '/' .. submodule_url .. ' ' .. module .. '/' .. path) then
                io.stderr:write('can\'t clone submodule ' .. submodule .. '\n')
                return 1
            end
            path = nil
            submodule_url = nil
        end
    end
end