Maneira fácil de obter o mais recente de todos os submódulos git

1846

Estamos usando submódulos git para gerenciar alguns grandes projetos que dependem de muitas outras bibliotecas que desenvolvemos. Cada biblioteca é um repositório separado trazido para o projeto dependente como um submódulo. Durante o desenvolvimento, geralmente queremos apenas pegar a versão mais recente de cada submódulo dependente.

O git tem um comando interno para fazer isso? Caso contrário, que tal um arquivo em lotes do Windows ou similar que possa fazer isso?

Brad Robinson
fonte
O git-deep deve ajudar com isso.
Mathew Kurian
9
@Brad você deseja atualizar suas cópias dos submódulos para as revisões de confirmação nomeadas no projeto mestre; ou você deseja obter o commit HEAD mais recente de todos os submódulos? A maioria das respostas aqui aborda a primeira; muitas pessoas querem o último.
chrisinmtown

Respostas:

2464

Se for a primeira vez que você faz o check-out de um repositório, você precisa --initprimeiro:

git submodule update --init --recursive

Para o git 1.8.2 ou superior, a opção --remotefoi adicionada para oferecer suporte à atualização das dicas mais recentes de ramificações remotas:

git submodule update --recursive --remote

Isso tem o benefício adicional de respeitar as ramificações "não padrão" especificadas nos arquivos .gitmodulesou .git/config(se houver alguma, o padrão é origem / mestre; nesse caso, algumas das outras respostas aqui também funcionariam).

Para o git 1.7.3 ou superior, você pode usar (mas as dicas abaixo sobre qual atualização ainda se aplica):

git submodule update --recursive

ou:

git pull --recurse-submodules

se você deseja puxar seus submódulos para as confirmações mais recentes, em vez da confirmação atual, o repo aponta para.

Veja git-submódulo (1) para detalhes

Henrik Gustafsson
fonte
299
Provavelmente você deve usar git submodule update --recursivehoje em dia.
Jens Kohl
38
Melhoria de desempenho:git submodule foreach "(git checkout master; git pull)&"
Bogdan Gusiev 7/11/11
18
update atualizará cada submódulo para a revisão especificada, não para a mais recente desse repositório.
Peter DeWeese
21
Apenas para adicionar, a adesão às cegas origin masterno final deste comando pode ter resultados inesperados se alguns de seus submódulos estiverem rastreando um nome diferente de filial ou local desse submódulo específico. Óbvio para alguns, mas provavelmente não para todos.
Nathan Hornby
31
Apenas para esclarecer para todos. git submodule update --recursiveprocura ver qual revisão o repositório pai armazenou para cada submódulo e depois faz check-out dessa revisão em cada submódulo. Ele não puxar as últimas commits para cada sub-módulo. git submodule foreach git pull origin masterou git pull origin master --recurse-submodulesé o que você deseja se pretende atualizar cada submódulo para o mais recente a partir de seus repositórios de origem. Somente então você receberá alterações pendentes no repositório pai com hashes de revisão atualizados para submódulos. Verifique aqueles e você é bom.
Chev,
636
git pull --recurse-submodules --jobs=10

um recurso que o git aprendeu pela primeira vez no 1.8.5.

Até que o bug seja corrigido, pela primeira vez você precisa executar

Atualização do sub-módulo git --init --recursive

Alexander Bartosh
fonte
29
upvoted, i usar esta: de alias update_submodules = 'git pull --recurse-submódulos && git submódulo actualização'
Stephen C
3
Isso funciona se os submódulos já tiverem sido puxados pelo menos uma vez, mas para submódulos que nunca foram retirados, consulte a resposta da gahooa abaixo.
Matt Browne
8
Isso acessará a versão especificada pelo repositório superior; NÃO puxa CABEÇA. Por exemplo, se o TopRepo especificar uma versão 2 atrás de HEAD para SubRepo, isso puxará o SubRepo com a versão 2 atrás. Outras respostas aqui puxam HEAD no SubRepo.
Chris Moschini
11
Note-se que nem git pull --recurse-submodulesnem git submodule update --recursiveque não initialize recém submódulos acrescentou. Para inicializá-los, você precisa executar git submodule update --recursive --init. Citação do manual : Se o submódulo ainda não foi inicializado e você deseja apenas usar a configuração armazenada em .gitmodules, você pode inicializar automaticamente o submódulo com a opção --init.
Patryk.beza
1
talvez adicione uma dica à git submodule update --recursive --remotequal também atualize os submódulos para a revisão mais recente remota, em vez do SHA-1 armazenado.
21716 Hanno S.
386

No init, execute o seguinte comando:

git submodule update --init --recursive

de dentro do diretório git repo, funciona melhor para mim.

Isso puxará todas as mais recentes, incluindo os submódulos.

Explicado

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.

Depois disso, você pode simplesmente executar:

git submodule update --recursive

de dentro do diretório git repo, funciona melhor para mim.

Isso puxará todas as mais recentes, incluindo os submódulos.

abc123
fonte
10
Sim - a resposta mais votada foi a melhor maneira de fazê-lo em 2009, mas essa é definitivamente mais simples e mais intuitiva agora.
Michael Scott Cuthbert
2
@MichaelScottCuthbert graças, eu tenho certeza que em mais 3 anos este comando será louco também
abc123
5
No entanto, isso não faz check-out da revisão mais recente do submódulo, apenas da revisão mais recente que o pai está rastreando.
Nathan Osman
4
@NathanOsman, que é o que você deseja ... você terminará com código quebrado, não seguindo o rastreamento de revisão dos pais. Se você é o mantenedor dos pais, pode atualizá-los e confirmá-los.
Abc123
2
Sim, mas, pelo que entendi, não era isso que o OP queria.
Nathan Osman
305

Nota: Isso é de 2009 e pode ter sido bom, mas agora existem opções melhores.

Nós usamos isso. É chamado 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

Basta colocá-lo em um diretório bin adequado (/ usr / local / bin). Se estiver no Windows, pode ser necessário modificar a sintaxe para que funcione :)

Atualizar:

Em resposta ao comentário do autor original sobre puxar todas as HEADs de todos os submódulos - essa é uma boa pergunta.

Tenho certeza de que gitnão tem um comando para isso internamente. Para fazer isso, você precisaria identificar o que realmente é um HEAD para um submódulo. Isso pode ser tão simples quanto dizer que masteré o ramo mais atualizado, etc ...

Depois disso, crie um script simples que faça o seguinte:

  1. verifique se git submodule statushá repositórios "modificados". O primeiro caractere das linhas de saída indica isso. Se um sub-repo for modificado, você NÃO poderá continuar.
  2. para cada repo listado, cd no diretório e execute git checkout master && git pull. Verifique se há erros.
  3. No final, sugiro que você imprima uma tela para o usuário para indicar o status atual dos submódulos - talvez solicite que eles adicionem tudo e confirmem?

Eu gostaria de mencionar que esse estilo não é realmente o que os sub-módulos git foram projetados. Normalmente, você quer dizer que "LibraryX" está na versão "2.32" e permanecerá assim até que eu diga para "atualizar".

Isso é, de certa forma, o que você está fazendo com o script descrito, mas apenas mais automaticamente. É necessário cuidado!

Atualização 2:

Se você estiver em uma plataforma Windows, pode querer usar o Python para implementar o script, pois ele é muito capaz nessas áreas. Se você estiver no unix / linux, sugiro apenas um script bash.

Precisa de algum esclarecimento? Basta postar um comentário.

gahooa
fonte
Eu não acho que é isso que eu quero. Isso não puxará a versão dos submódulos com os quais o superprojeto foi comprometido pela última vez. Eu quero puxar a versão principal de todos os submódulos.
23310 Brad Robinson
3
Isso funciona muito bem e funciona não apenas para atualizar os submódulos, mas também para buscá-los pela primeira vez, se é isso que você precisa.
Matt Browne
Acabei de receber "Não há informações de rastreamento para o ramo atual. Especifique com qual ramo você deseja mesclar". Não importa o que eu tente: /
Nathan Hornby
9
Por que não criar um alias para isso? git config --global alias.pup '!git pull && git submodule init && git submodule update && git submodule status'e use-o como git pupsem nenhum script.
Fracz
Obrigado, por algum motivo, mesmo que eu tenha o git 1.9.1, tive que executar git submodule initapós o primeiro pull que incluía submódulos, para que tudo começasse a funcionar corretamente.
Ben Usman
164

Henrik está no caminho certo. O comando 'foreach' pode executar qualquer script shell arbitrário. Duas opções para obter as últimas podem ser:

git submodule foreach git pull origin master

e,

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

Isso irá percorrer todos os submódulos inicializados e executar os comandos fornecidos.

mturquette
fonte
144

O seguinte funcionou para mim no Windows.

git submodule init
git submodule update
zachleat
fonte
6
Isso claramente não é o que o OP solicitou. Ele será atualizado apenas para o submódulo associado, e não o mais recente.
28411 Patrick
52
Esta é no entanto a única coisa sobre esta página que tem git para submódulos puxar a primeira vez que eu verifiquei um repo
theheadofabroom
2
Também é possível usar: update submodule git --init --recursive (particularmente se o sub-módulo em questão é RestKit a partir de um clone fresco)
HCdev
33

Editar :

Nos comentários foi apontado (por philfreo ) que a versão mais recente é necessária. Se houver algum submódulo aninhado que precise estar em sua versão mais recente:

git submodule foreach --recursive git pull

----- Comentário desatualizado abaixo -----

Essa não é a maneira oficial de fazer isso?

git submodule update --init

Eu uso sempre. Sem problemas até agora.

Editar:

Acabei de descobrir que você pode usar:

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

O que também puxará recursivamente todos os submódulos, ou seja, dependências.

antitóxico
fonte
5
A sua resposta não responder à pergunta do OP, mas fazer o que você já proposto, você pode simplesmente dizergit submodule update --init --recursive
philfreo
2
Entendo, a versão mais recente é necessária. Bem, isso pode ser útil se houver submódulos aninhadas: git submodule foreach --recursive git pull
antitoxic
1
Eu não poderia fazer nada disso baixar nada - "a atualização do sub-módulo git --init --recursive" funcionou para mim, no entanto.
precisa saber é o seguinte
33

Como pode acontecer que o ramo padrão dos seus submódulos não seja master, é assim que automatizo as atualizações completas dos submódulos do 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'
Sebastien Varrette
fonte
Fora das muitas respostas para as muitas perguntas, este trabalhou para mim (2019, erro github com ids específica de hash)
philshem
30

Primeira vez

Sub-módulo Clone e Init

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

Descansar

Durante o desenvolvimento, basta puxar e atualizar o submódulo

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

Atualize o submódulo Git para o commit mais recente na origem

git submodule foreach git pull origin master

A maneira preferida deve estar abaixo

git submodule update --remote --merge

nota: os dois últimos comandos têm o mesmo comportamento

Yash
fonte
Eu fiz um clone git sem sub-módulos por engano e todas as outras opções não funcionaram, ninguém clonou sub-módulos. Usando o seu, git submodule updatefiz o truque. Agora estou baixando dados de sub-módulos ausentes no primeiro passo do clone. Obrigado. Eu não sou bom em git: C
m3nda 21/01
Esse anser é realmente uma resposta muito boa para fazer uma pergunta aqui em cima: por que eu tenho que ".. - submódulos recursivos .." e depois adicionalmente a "... atualização ..." e até ".. .foreach ... "mais tarde para obter a confirmação mais recente? Tudo isso não parece GIT! O que o "update" está fazendo e por que preciso ir manualmente a cada módulo para obter? Não é isso que "... - submódulos de receita ..." está fazendo? Alguma dica?
Peter Branforn
20

Não sei desde qual versão do git está funcionando, mas é isso que você está procurando:

git submodule update --recursive

Também o uso git pullpara atualizar o repositório raiz:

git pull && git submodule update --recursive
Jens Kohl
fonte
10

As respostas acima são boas, no entanto, estávamos usando o git-hooks para facilitar isso, mas acontece que no git 2.14 , você pode definir git config submodule.recursecomo true para permitir que os sub-módulos sejam atualizados quando você puxa para o seu repositório git.

Isso terá o efeito colateral de empurrar todas as alterações dos submódulos que você tiver, se estiverem em ramificações, mas se você já precisar desse comportamento, isso poderá funcionar.

Pode ser feito usando:

git config submodule.recurse true
JamesD
fonte
Preciso adorar essa opção, infelizmente ainda precisamos usar git submodule initantes, se o seu submódulo ainda não estiver inicializado.
Pellet
5

Git para windows 2.6.3 :

git submodule update --rebase --remote

Seul
fonte
Esse é o único que funcionou para mim. Eu não era mesmo capaz de inicialização ou atualização como o ponteiro submodule estava apontando para uma versão que não estava no controle remoto mais
Pavel P
4

Do nível superior no repositório:

git submodule foreach git checkout develop
git submodule foreach git pull

Isso mudará todos os ramos para desenvolver e puxar as últimas

Srayan Guhathakurta
fonte
2
Não funciona para mim, com o git 2.7.
Bruno Haible 20/03/19
Você tem algo como um arquivo Everything sln que adiciona todas as referências de projeto na árvore? Também que erro você vê? Você também pode verificar o seu arquivo gitignore
#
1
git submodule foreach git pull origin masterTinha que acrescentar o ramo que eu queria buscar. Fora isso, funcionou perfeitamente.
Torxed 01/10/19
3

Fiz isso adaptando a resposta de gahooa acima :

Integre-o com um git [alias]...

Se o seu projeto pai tiver algo parecido com isto em .gitmodules:

[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

Adicione algo assim dentro do seu .gitconfig

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

Em seguida, para atualizar seus submódulos, execute:

git updatesubs

Eu tenho um exemplo disso no meu repositório de configuração do ambiente .

Tom
fonte
3

Tudo que você precisa fazer agora é uma simples git checkout

Apenas certifique-se de habilitá-lo através desta configuração global: git config --global submodule.recurse true

Pellet
fonte
2

Aqui está a linha de comando para extrair de todos os seus repositórios git, sejam eles ou não submódulos:

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

Se você executá-lo em seu repositório git superior, poderá substituí- "$ROOT"lo ..

kenorb
fonte
1

Eu acho que você terá que escrever um script para fazer isso. Para ser sincero, posso instalar o python para fazer isso, para que você possa usá os.walk-lo cdem cada diretório e emitir os comandos apropriados. O uso de python ou alguma outra linguagem de script, que não seja lote, permitiria adicionar / remover facilmente subprojetos sem a necessidade de modificar o script.

baudtack
fonte
1

Observação: maneira não muito fácil, mas viável e possui seus próprios profissionais exclusivos.

Se alguém deseja clonar apenas a HEADrevisão de um repositório e apenas HEADs de todos os seus sub-módulos (por exemplo, checkout "trunk"), então pode-se usar o seguinte script Lua . Às vezes, um comando simples git submodule update --init --recursive --remote --no-fetch --depth=1pode resultar em um giterro irrecuperável . Nesse caso, é necessário limpar o subdiretório do .git/modulesdiretório e clonar o submódulo manualmente usando o git clone --separate-git-dircomando A única complexidade é descobrir URL , caminho do .gitdiretório do submódulo e caminho do submódulo na árvore do superprojeto.

Observação: o script é testado apenas no https://github.com/boostorg/boost.gitrepositório. Suas peculiaridades: todos os submódulos hospedados no mesmo host e .gitmodulescontém apenas URLs relativos .

-- 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
Tomilov Anatoliy
fonte