Un modo semplice per estrarre i sottomoduli Git più recenti


1847

Stiamo usando i sottomoduli git per gestire un paio di grandi progetti che dipendono da molte altre librerie che abbiamo sviluppato. Ogni libreria è un repository separato portato nel progetto dipendente come sottomodulo. Durante lo sviluppo, spesso vogliamo semplicemente andare a prendere l'ultima versione di ogni sottomodulo dipendente.

Git ha un comando integrato per farlo? In caso contrario, che ne dici di un file batch di Windows o simile che può farlo?


git-deep dovrebbe aiutare in questo.
Mathew Kurian,

9
@Brad vuoi aggiornare le tue copie dei sottomoduli ai rev di commit indicati nel progetto principale; o vuoi estrarre l'ultimo commit HEAD da ogni sottomodulo? La maggior parte delle risposte qui riguarda la prima; molte persone vogliono quest'ultima.
chrisinmtown,

Risposte:


2465

Se è la prima volta che effettui il checkout di un repository, devi --initprima utilizzare :

git submodule update --init --recursive

Per git 1.8.2 o versioni successive, è --remotestata aggiunta l'opzione per supportare l'aggiornamento agli ultimi suggerimenti delle filiali remote:

git submodule update --recursive --remote

Ciò ha l'ulteriore vantaggio di rispettare tutti i rami "non predefiniti" specificati in .gitmoduleso.git/config file (se ti capita di avere qualcuno, il default è origin / master, nel qual caso anche alcune delle altre risposte qui funzionerebbero).

Per git 1.7.3 o versioni successive puoi usare (ma i gotcha di seguito su quale aggiornamento si applica ancora):

git submodule update --recursive

o:

git pull --recurse-submodules

se si desidera eseguire il pull dei sottomoduli agli ultimi commit anziché all'attuale commit a cui punta il repository.

Vedi git-submodule (1) per i dettagli


299
Probabilmente dovresti usare al git submodule update --recursivegiorno d'oggi.
Jens Kohl,

38
Miglioramento delle prestazioni:git submodule foreach "(git checkout master; git pull)&"
Bogdan Gusiev

18
update aggiornerà ogni sottomodulo alla revisione specificata, non aggiornerà all'ultimo per quel repository.
Peter DeWeese,

21
Solo per aggiungere, attaccare origin masteralla cieca alla fine di questo comando potrebbe avere risultati imprevisti se alcuni dei tuoi sottomoduli stanno seguendo un nome di filiale o posizione diverso di quel particolare sottomodulo. Ovvio per alcuni, ma probabilmente non per tutti.
Nathan Hornby,

31
Solo per chiarire per tutti. git submodule update --recursivecerca di vedere quale revisione ha archiviato il repository padre per ciascun sottomodulo, quindi verifica quella revisione in ciascun sottomodulo. NON esegue gli ultimi commit per ciascun sottomodulo. git submodule foreach git pull origin mastero git pull origin master --recurse-submodulesè quello che vuoi se intendi aggiornare ogni sottomodulo all'ultimo dai loro repository di origine. Solo così otterrai modifiche in sospeso nel repository principale con hash di revisione aggiornati per i sottomoduli. Dai un'occhiata a quelli e sei a posto.
Chev,

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

una funzionalità git appresa per la prima volta in 1.8.5.

Fino a quando il bug non viene corretto, per la prima volta è necessario eseguire

git submodule update --init --recursive


29
votato, uso questo: alias update_submodules = 'git pull --recurse-submodules && git submodule update'
Stephen C

3
Questo funziona se i sottomoduli sono già stati estratti almeno una volta, ma per i sottomoduli che non sono mai stati estratti, vedere la risposta di gahooa di seguito.
Matt Browne,

8
Questo accederà alla versione specificata dal repository principale; NON tira TESTA. Ad esempio, se TopRepo specifica una versione 2 dietro HEAD per SubRepo, ciò tirerà SubRepo con quella versione 2 dietro. Altre risposte qui tirano HEAD in SubRepo.
Chris Moschini,

11
Nota che né git pull --recurse-submodulesgit submodule update --recursivenon non inizializzazione di nuova sottomoduli aggiunto. Per inizializzarli è necessario eseguire git submodule update --recursive --init. Citazione da manuale : se il sottomodulo non è ancora inizializzato e si desidera semplicemente utilizzare l'impostazione memorizzata in .gitmodules, è possibile inizializzare automaticamente il sottomodulo con l'opzione --init.
patryk.beza,

1
forse aggiungere un suggerimento al git submodule update --recursive --remotequale aggiorna anche i sottomoduli all'ultima revisione remota invece che allo SHA-1 memorizzato.
Hanno S.

386

Su init esegue il seguente comando:

git submodule update --init --recursive

dall'interno della directory git repo, funziona meglio per me.

Questo tirerà tutti gli ultimi inclusi i sottomoduli.

spiegato

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.

Dopo questo puoi semplicemente eseguire:

git submodule update --recursive

dall'interno della directory git repo, funziona meglio per me.

Questo tirerà tutti gli ultimi inclusi i sottomoduli.


10
Sì, la risposta più votata è stata il modo migliore per farlo nel '09, ma ora è decisamente più semplice e intuitivo.
Michael Scott Cuthbert,

2
@MichaelScottCuthbert grazie, sono sicuro che tra altri 3 anni anche questo comando sarà pazzo
abc123

5
Tuttavia, questo non verifica l'ultima revisione dal sottomodulo, ma solo l'ultima revisione che il genitore sta monitorando.
Nathan Osman,

4
@NathanOsman che è quello che vuoi ... finirai con il codice rotto non seguendo il tracciamento della revisione dei genitori. Se sei il manutentore del genitore, puoi aggiornarlo e impegnarlo.
abc123,

2
Sì, ma da quanto ho capito, non è quello che voleva l'OP.
Nathan Osman,

305

Nota: questo è del 2009 e potrebbe essere stato buono allora, ma ora ci sono opzioni migliori.

Usiamo questo. Si chiama 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 inserirlo in una directory bin adatta (/ usr / local / bin). Se su Windows, potrebbe essere necessario modificare la sintassi per farlo funzionare :)

Aggiornare:

In risposta al commento dell'autore originale su come inserire tutti i HEAD di tutti i sottomoduli - questa è una buona domanda.

Sono abbastanza sicuro che gitnon abbia un comando per questo internamente. Per fare ciò, dovresti identificare cosa è realmente HEAD per un sottomodulo. Potrebbe essere semplice come dire che masterè il ramo più aggiornato, ecc ...

Successivamente, crea un semplice script che procede come segue:

  1. verifica la presenza git submodule statusdi repository "modificati". Il primo carattere delle righe di output indica questo. Se un sub-repo viene modificato, potresti NON voler procedere.
  2. per ogni repository elencato, cd nella relativa directory ed esecuzione git checkout master && git pull. Controlla errori.
  3. Alla fine, ti suggerisco di stampare un display per indicare lo stato corrente dei sottomoduli - forse spingerli ad aggiungere tutto e impegnarsi?

Vorrei menzionare che questo stile non è proprio quello per cui sono stati progettati i sottomoduli git. In genere, vuoi dire "LibraryX" è alla versione "2.32" e rimarrà tale fino a quando non gli dirò di "aggiornare".

Questo è, in un certo senso, quello che stai facendo con lo script descritto, ma solo più automaticamente. È necessaria la cura!

Aggiornamento 2:

Se sei su una piattaforma Windows, potresti voler usare Python per implementare lo script in quanto è molto capace in queste aree. Se sei su unix / linux, allora suggerisco solo uno script bash.

Hai bisogno di chiarimenti? Pubblica un commento.


Non penso sia quello che voglio. Non tirerà fuori la versione dei sottomoduli con cui il super-progetto è stato impegnato l'ultima volta. Voglio estrarre la versione head di tutti i sottomoduli.
Brad Robinson,

3
Funziona benissimo e funziona non solo per aggiornare i sottomoduli ma anche per recuperarli per la prima volta se è quello che ti serve.
Matt Browne,

Sto solo ricevendo "Non ci sono informazioni di tracciamento per il ramo corrente. Specifica con quale ramo vuoi unirti." Non importa cosa provo: /
Nathan Hornby

9
Perché non creare un alias per questo? git config --global alias.pup '!git pull && git submodule init && git submodule update && git submodule status'e poi usalo come git pupsenza script.
venerdì

Grazie, per qualche motivo, anche se ho git 1.9.1, ho dovuto esibirmi git submodule initdopo il primo pull che includeva i sottomoduli, in modo che tutto potesse iniziare a funzionare correttamente.
Ben Usman,

164

Henrik è sulla buona strada. Il comando 'foreach' può eseguire qualsiasi script shell arbitrario. Due opzioni per estrarre l'ultima potrebbe essere,

git submodule foreach git pull origin master

e,

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

Ciò eseguirà l'iterazione attraverso tutti i sottomoduli inizializzati ed eseguirà i comandi dati.


144

Quanto segue ha funzionato per me su Windows.

git submodule init
git submodule update

6
Questo chiaramente non è ciò che l'OP ha richiesto. Si aggiornerà solo al commit del sottomodulo associato e non a quello più recente.
Patrick,

52
Questa è comunque l'unica cosa in questa pagina che ha avuto il coraggio di estrarre i sottomoduli la prima volta che ho controllato un repository
theheadofabroom

2
Può anche usare: git submodule update --init --recursive (in particolare se il sottomodulo in questione è RestKit da un nuovo clone)
HCdev

33

Modifica :

Nei commenti è stato sottolineato (da philfreo ) che è richiesta l'ultima versione. Se sono presenti sottomoduli nidificati che devono trovarsi nella loro ultima versione:

git submodule foreach --recursive git pull

----- Commento obsoleto di seguito -----

Non è questo il modo ufficiale per farlo?

git submodule update --init

Lo uso ogni volta. Nessun problema finora.

Modificare:

Ho appena scoperto che puoi usare:

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

Che tirerà anche in modo ricorsivo tutti i sottomoduli, cioè le dipendenze.


5
La tua risposta non risponde alla domanda del PO, ma per fare ciò che hai proposto puoi semplicemente diregit submodule update --init --recursive
philfreo,

2
Vedo, è necessaria l'ultima versione. Bene, questo potrebbe essere utile se ci sono sottomoduli nidificati: git submodule foreach --recursive git pull
antitossico

1
In realtà non sono riuscito a far scaricare nulla di tutto ciò: "git submodule update --init --recursive" ha funzionato per me.
BrainSlugs83,

33

Come può accadere che il ramo predefinito dei sottomoduli non sia master , ecco come automatizzo gli aggiornamenti completi dei sottomoduli 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'

tra le molte risposte a molte domande, questa ha funzionato per me (2019, errore github con specifici hash id)
philshem,

30

Prima volta

Clone e Init Submodule

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

riposo

Durante lo sviluppo basta tirare e aggiornare il sottomodulo

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

Aggiorna il sottomodulo Git all'ultimo commit sull'origine

git submodule foreach git pull origin master

Il modo preferito dovrebbe essere al di sotto

git submodule update --remote --merge

nota: gli ultimi due comandi hanno lo stesso comportamento


Ho fatto un clone git senza sottomoduli per errore e tutte le altre opzioni non hanno funzionato, nessuno ha clonato sottomoduli. Usando il tuo, git submodule updatehai fatto il trucco. Ora sto scaricando i dati dei sottomoduli mancanti dal primo passo del clone. Grazie. Non sono bravo a git: C
m3nda

Questa risposta è in realtà un'ottima risposta per porre una domanda qui sopra: perché devo ".. --recursive-submodules .." e poi anche "... aggiorna ..." e persino ".. .foreach ... "più tardi per ottenere l'ultimo commit? Tutto ciò non sembra affatto GIT! Cosa sta facendo "update" e perché devo andare manualmente a ciascun modulo per estrarre? Non è quello che sta facendo "... --recurse-submodules .."? Qualche suggerimento?
Peter Branforn,

20

Non so da quale versione di git funzioni, ma è quello che stai cercando:

git submodule update --recursive

Lo uso anche git pullper aggiornare il repository root:

git pull && git submodule update --recursive

10

Le risposte di cui sopra sono buone, tuttavia abbiamo usato git-hook per renderlo più semplice, ma risulta che in git 2.14 , puoi impostare git config submodule.recursetrue per abilitare l'aggiornamento dei sottomoduli quando passi al tuo repository git.

Ciò avrà l'effetto collaterale di spingere tutti i cambiamenti dei sottomoduli che hai se sono comunque sui rami, ma se hai già bisogno di quel comportamento già questo potrebbe fare il lavoro.

Può essere fatto usando:

git config submodule.recurse true

Devo amare questa opzione, purtroppo è ancora necessario utilizzare git submodule initprima se però il tuo sottomodulo non è ancora inizializzato.
Pellet

5

Git per Windows 2.6.3 :

git submodule update --rebase --remote


Questo è l'unico che ha funzionato per me. Non ero nemmeno in grado di avviare o aggiornare poiché il puntatore del sottomodulo puntava a una versione che non era più nel telecomando
Pavel P

4

Dal livello superiore nel repository:

git submodule foreach git checkout develop
git submodule foreach git pull

Questo cambierà tutti i rami per sviluppare e tirare gli ultimi


2
Non funziona per me, con Git 2.7.
Bruno Haible,

Hai qualcosa come un file sln Tutto che aggiunge tutti i riferimenti al progetto nella struttura? Inoltre quale errore vedi? Puoi controllare anche il tuo file
gitignore

1
git submodule foreach git pull origin masterHo dovuto aggiungere il ramo che volevo recuperare. a parte questo, ha funzionato perfettamente.
Torxed

3

L'ho fatto adattando la risposta di gahooa sopra :

Integralo con un git [alias]...

Se il tuo progetto genitore ha qualcosa del genere in .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

Aggiungi qualcosa del genere all'interno del tuo .gitconfig

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

Quindi per aggiornare i sottomoduli, eseguire:

git updatesubs

Ne ho un esempio nel repository di installazione del mio ambiente .


3

Tutto quello che devi fare ora è un semplice git checkout

Assicurati di abilitarlo tramite questa configurazione globale: git config --global submodule.recurse true


2

Ecco la riga di comando per estrarre da tutti i tuoi repository git che siano o meno sottomoduli:

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

Se lo esegui nel tuo repository git principale, puoi sostituirlo "$ROOT"con ..


1

Penso che dovrai scrivere una sceneggiatura per farlo. Per essere onesti, potrei installare python di farlo in modo che è possibile utilizzare os.walkper cdper ogni directory ed emettere i comandi appropriati. L'uso di Python o di un altro linguaggio di scripting, diverso da batch, ti consentirebbe di aggiungere / rimuovere facilmente sottoprogetti senza dover modificare lo script.


1

Nota: non troppo semplice, ma fattibile e ha i suoi pro unici.

Se si desidera clonare solo la HEADrevisione di un repository e solo HEADs di tutti i suoi sottomoduli (vale a dire checkout "trunk"), è possibile utilizzare il seguente script Lua . A volte un semplice comando git submodule update --init --recursive --remote --no-fetch --depth=1può provocare un giterrore irrecuperabile . In questo caso è necessario ripulire la sottodirectory di.git/modules directory e clonare il sottomodulo manualmente usando il git clone --separate-git-dircomando. L'unica complessità è scoprire l' URL , il percorso della .gitdirectory del sottomodulo e il percorso del sottomodulo nell'albero del superprogetto.

Nota: lo script viene testato solo in base al https://github.com/boostorg/boost.gitrepository. Le sue peculiarità: tutti i sottomoduli ospitati sullo stesso host e .gitmodulescontengono solo URL relativi .

-- 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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.