Easy way to pull latest of all git submodules

2018-12-31 14:24发布

We're using git submodules to manage a couple of large projects that have dependencies on many other libraries we've developed. Each library is a separate repo brought into the dependent project as a submodule. During development, we often want to just go grab the latest version of every dependent submodule.

Does git have a built in command to do this? If not, how about a Windows batch file or similar that can do it?

18条回答
谁念西风独自凉
2楼-- · 2018-12-31 15:05

I think you'll have to write a script to do this. To be honest, I might install python to do it so that you can use os.walk to cd to each directory and issue the appropriate commands. Using python or some other scripting language, other than batch, would allow you to easily add/remove subprojects with out having to modify the script.

查看更多
临风纵饮
3楼-- · 2018-12-31 15:06

The following worked for me on Windows.

git submodule init
git submodule update
查看更多
妖精总统
4楼-- · 2018-12-31 15:08

Note: This is from 2009 and may have been good then but there are better options now.

We use this. It's called 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

Just put it in a suitable bin directory (/usr/local/bin). If on Windows, you may need to modify the syntax to get it to work :)

Update:

In response to the comment by the original author about pulling in all of the HEADs of all of the submodules -- that is a good question.

I am pretty sure that git does not have a command for this internally. In order to do so, you would need to identify what HEAD really is for a submodule. That could be as simple as saying master is the most up to date branch, etc...

Following this, create a simple script that does the following:

  1. check git submodule status for "modified" repositories. The first character of the output lines indicates this. If a sub-repo is modified, you may NOT want to proceed.
  2. for each repo listed, cd into it's directory and run git checkout master && git pull. Check for errors.
  3. At the end, I suggest you print a display to the user to indicate the current status of the submodules -- perhaps prompt them to add all and commit?

I'd like to mention that this style is not really what git submodules were designed for. Typically, you want to say "LibraryX" is at version "2.32" and will stay that way until I tell it to "upgrade".

That is, in a sense, what you are doing with the described script, but just more automatically. Care is required!

Update 2:

If you are on a windows platform, you may want to look at using Python to implement the script as it is very capable in these areas. If you are on unix/linux, then I suggest just a bash script.

Need any clarifications? Just post a comment.

查看更多
明月照影归
5楼-- · 2018-12-31 15:08

Remark: not too easy way, but workable and it has its own unique pros.

If one want to clone only HEAD revision of a repository and only HEADs of all the its submodules (i.e. to checkout "trunk"), then one can use following Lua script. Sometimes simple command git submodule update --init --recursive --remote --no-fetch --depth=1 can result in an unrecoverable git error. In this case one need to clean up subdirectory of .git/modules directory and clone submodule manually using git clone --separate-git-dir command. The only complexity is to find out URL, path of .git directory of submodule and path of submodule in superproject tree.

Remark: the script is only tested against https://github.com/boostorg/boost.git repository. Its peculiarities: all the submodules hosted on the same host and .gitmodules contains only relative URLs.

-- 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
查看更多
无色无味的生活
6楼-- · 2018-12-31 15:09

Git for windows 2.6.3:

git submodule update --rebase --remote

查看更多
无色无味的生活
7楼-- · 2018-12-31 15:13

As it may happens that the default branch of your submodules is not master, this is how I automate the full Git submodules upgrades:

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'
查看更多
登录 后发表回答