轻松获取最新的git子模块

Easy way to pull latest of all git submodules

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

Git有内置的命令吗?如果不是,那么可以用Windows批处理文件或类似的文件来完成呢?


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

1
git submodule update --recursive --remote

这还有一个额外的好处,那就是尊重.gitmodules.git/config文件中指定的任何"非违约"分支(如果您碰巧有,违约是源站/主服务器,在这种情况下,这里的一些其他答案也会起作用)。

对于Git 1.7.3或更高版本,您可以使用(但下面的内容仍然适用于更新的内容):

1
git submodule update --recursive

或:

1
git pull --recurse-submodules

如果你想把你的子模块拉到回购所指向的最新提交集成上。

注:如果这是您第一次签出回购,您需要先使用--init

1
git submodule update --init --recursive

对于较旧的Git 1.6.1或更高版本,您可以使用类似于(修改为适合)的内容:

1
git submodule foreach git pull origin master

详见Git子模块(1)


如果需要将子模块的内容拉入子模块存储库,请使用

1
git pull --recurse-submodules

a feature git first learned in 1.7.3.

但这不会在子模块中签出正确的提交(主存储库指向的提交)。

要检查子模块中的正确提交,应在使用

1
git submodule update --recursive --remote


在init上运行以下命令:

1
git submodule update --init --recursive

从git repo目录中,对我来说最有效。

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

解释

1
2
3
4
5
6
7
8
9
10
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.

在此之后,您可以运行:

1
git submodule update --recursive

从git repo目录中,对我来说最有效。

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

解释

1
2
3
4
5
6
7
8
9
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.
            any submodule locations.
            --recursive is specified, this command will recurse into the registered
            submodules, and update any nested submodules within.


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

我们用这个。叫git-pup

1
2
3
4
#!/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上,您可能需要修改语法以使其正常工作:)

更新:

为了回应原作者关于拉入所有子模块的所有头部的评论,这是一个很好的问题。

我敢肯定,江户十一〔一〕内部并没有这方面的命令。为了做到这一点,您需要确定子模块的头部到底是什么。这可以简单地说,master是最新的分支机构,等等。

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

  • 检查git submodule status中的"已修改"存储库。输出行的第一个字符表示这一点。如果修改了子回购,您可能不想继续。
  • 对于列出的每个回购,将CD放入其目录并运行git checkout master && git pull。检查错误。
  • 最后,我建议您打印一个显示给用户,以指示子模块的当前状态——也许会提示他们全部添加并提交?
  • 我想说的是,这种风格并不是专为Git子模块设计的。通常,您想说"libraryx"的版本是"2.32",在我告诉它"升级"之前,它将一直保持这种状态。

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

    更新2:

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

    需要澄清吗?发表评论。


    亨利走对了。"foreach"命令可以执行任意shell脚本。最新的两个选择可能是:

    1
    git submodule foreach git pull origin master

    而且,

    1
    git submodule foreach /path/to/some/cool/script.sh

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


    以下是我在窗户上的作品。

    1
    2
    git submodule init
    git submodule update


    编辑:

    在评论中指出(菲尔弗雷奥)需要最新版本。如果有任何嵌套子模块需要在其最新版本中:

    1
    git submodule foreach --recursive git pull

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

    这不是正式的方式吗?

    1
    git submodule update --init

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

    编辑:

    我发现你可以使用:

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

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


    由于您的子模块的默认分支可能不是master,所以这是我如何自动执行完整的git子模块升级的方法:

    1
    2
    3
    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'

    第一次

    克隆和初始化子模块

    1
    2
    git clone [email protected]:speedovation/kiwi-resources.git resources
    git submodule init

    休息

    在开发过程中,只需拉动和更新子模块

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

    将git子模块更新为源站上的最新提交

    1
    git submodule foreach git pull origin master

    首选方式应低于

    1
    git submodule update --remote --merge

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


    我不知道这是哪个版本的Git,但这是你要搜索的:

    1
    git submodule update --recursive

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

    1
    git pull && git submodule update --recursive

    查看http://lists.zerezo.com/git/msg674976.html,其中引入了--track参数


    适用于Windows 2.6.3的Git:

    git submodule update --rebase --remote


    上面的答案很好,但是我们使用了Git钩子来简化这个过程,但是在Git2.14中,您可以将git config submodule.recurse设置为true,以便在您拉入Git存储库时更新子模块。

    但是,如果所有子模块都在分支上,那么这将产生推动所有子模块更改的副作用,但是如果您已经需要这种行为,那么这就可以完成工作了。

    可通过以下方式完成:

    1
    git config submodule.recurse true

    我是通过调整gahooa的回答来做到这一点的:

    与Git [alias]集成…

    如果您的父项目在.gitmodules中有类似的内容:

    1
    2
    3
    4
    5
    6
    [submodule"opt/submodules/solarized"]
        path = opt/submodules/solarized
        url = [email protected]:altercation/solarized.git
    [submodule"opt/submodules/intellij-colors-solarized"]
        path = opt/submodules/intellij-colors-solarized
        url = [email protected]:jkaving/intellij-colors-solarized.git

    在.gitconfig中添加类似的内容

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

    然后,要更新子模块,请运行:

    1
    git updatesubs

    我在环境设置报告中有一个例子。


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

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

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


    从回购协议的顶层:

    1
    2
    git submodule foreach git checkout develop
    git submodule foreach git pull

    这将切换所有分支以开发和提取最新的


    备注:方法不太简单,但可行,有自己独特的优点。

    如果只想克隆存储库的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存储库进行测试。它的特点是:所有托管在同一主机上的子模块和.gitmodules只包含相对的URL。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    -- 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 .. '
    ')
        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 .. '
    ')
                    return 1
                end
                path = nil
                submodule_url = nil
            end
        end
    end

    你现在需要做的只是一个简单的git checkout

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


    我想你得写个剧本才能做到这一点。老实说,我可能会安装python来实现这一点,这样您就可以对每个目录使用os.walkcd,并发出适当的命令。使用python或批处理以外的其他脚本语言,可以轻松地添加/删除子项目,而无需修改脚本。