我们使用git子模块来管理一些依赖于我们开发的许多其他库的大型项目。每个库都是一个单独的repo,作为子模块引入到依赖项目中。在开发过程中,我们经常想要获取每个依赖子模块的最新版本。

如何获取所有git子模块的最新更改?


当前回答

正如antitoxic的回答所指出的,一个简单的git子模块foreach——递归git pull就足够了。

弗朗西斯·培根(Francis Bacon)的回答指出,git pull-递归子模块可能不同。

您可以使用详细选项进行测试并查看发生了什么:

git pull -v --recurse-submodules

但为此,您需要Git 2.40(2023年第1季度)。

“git pull-v--recurse submodule”(man)试图将-v传递给底层git submodule update(man),后者不理解请求并弹出:这已经用git 2.40(2023年第1季度)纠正。

参见Sven Strickroth(csware)提交的6f65f84(2022年12月10日)。(由Junio C Hamano--gitster--在提交b3b9e5c中合并,2022年12月28日)

子模块:接受更新命令的-v签字人:Sven Strickroth

由于a56771a(“builtin/pull:尊重子模块中的冗余设置”,2018-01-25,Git v2.17.0-rc0--批次#3中列出的merge),“Git pull-v--递归子模块”(man)将-v传播到子模块命令,但因为后者命令不理解该选项,所以它会退出。教“git子模块更新”(man)接受修复选项。

不再“barfing”(即显示用法'gitsubmodule foreach[-quiet][--recursive][--]<command>'),因为-v是gitsubmodule的未知选项。

其他回答

由于您的子模块的默认分支可能不是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'

备注:不太容易,但可行,它有自己独特的优点。

若要只克隆存储库的HEAD修订版和所有子模块的HEAD(即签出“trunk”),那个么可以使用以下Lua脚本。有时,简单的命令git submodule update--init--recursive--remote--no fetch--depth=1会导致不可恢复的git错误。在这种情况下,需要清理.git/modules目录的子目录,并使用gitclone--separate gitdir命令手动克隆子模块。唯一的复杂性是在超级项目树中查找子模块的URL、.git目录的路径和子模块的路径。

备注:脚本仅针对https://github.com/boostorg/boost.git存储库。它的特点:托管在同一主机上的所有子模块和.gitmodules只包含相对的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

正如antitoxic的回答所指出的,一个简单的git子模块foreach——递归git pull就足够了。

弗朗西斯·培根(Francis Bacon)的回答指出,git pull-递归子模块可能不同。

您可以使用详细选项进行测试并查看发生了什么:

git pull -v --recurse-submodules

但为此,您需要Git 2.40(2023年第1季度)。

“git pull-v--recurse submodule”(man)试图将-v传递给底层git submodule update(man),后者不理解请求并弹出:这已经用git 2.40(2023年第1季度)纠正。

参见Sven Strickroth(csware)提交的6f65f84(2022年12月10日)。(由Junio C Hamano--gitster--在提交b3b9e5c中合并,2022年12月28日)

子模块:接受更新命令的-v签字人:Sven Strickroth

由于a56771a(“builtin/pull:尊重子模块中的冗余设置”,2018-01-25,Git v2.17.0-rc0--批次#3中列出的merge),“Git pull-v--递归子模块”(man)将-v传播到子模块命令,但因为后者命令不理解该选项,所以它会退出。教“git子模块更新”(man)接受修复选项。

不再“barfing”(即显示用法'gitsubmodule foreach[-quiet][--recursive][--]<command>'),因为-v是gitsubmodule的未知选项。

上面的答案很好,不过我们使用了git钩子来简化这一过程,但事实证明,在git2.14中,您可以将git-config子模块.recure设置为true,以便在您拉到git存储库时更新子模块。

如果所有子模块都在分支上,这会产生一个副作用,即推动所有子模块的更改,但如果您已经需要这种行为,这可以完成任务。

可通过以下方式完成:

git config submodule.recurse true

注:这是从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上,您可能需要修改语法以使其正常工作:)

更新:

作为对原作者关于插入所有子模块的所有HEAD的评论的回应,这是一个很好的问题。

我很确定git内部没有这个命令。为了做到这一点,您需要确定HEAD对于子模块的真正含义。这可能很简单,比如说master是最新的分支,等等。。。

接下来,创建一个简单的脚本,执行以下操作:

检查git子模块状态以查找“已修改”的存储库。输出行的第一个字符表示这一点。如果子回购被修改,您可能不想继续。对于列出的每个回购,将其cd到其目录中,并运行gitcheckout-master和gitpull。检查错误。最后,我建议您向用户打印一个显示,以指示子模块的当前状态——也许会提示他们添加全部并提交?

我想指出的是,这种风格并不是git子模块的设计初衷。通常,你想说“LibraryX”的版本是“2.32”,在我告诉它“升级”之前,它会一直这样。

从某种意义上说,这就是您使用所描述的脚本所做的事情,但只是更加自动。需要小心!

更新2:

如果您使用的是windows平台,您可能需要考虑使用Python来实现脚本,因为它在这些方面非常有用。如果您使用的是unix/linux,那么我建议您使用bash脚本。

需要澄清吗?只需发表评论。