Sembra che in questa discussione si stiano mescolando due diversi scenari:
scenario 1
Utilizzando i puntatori del mio repository genitore ai sottomoduli, desidero controllare il commit in ciascun sottomodulo a cui punta il repository padre, possibilmente dopo aver prima iterato tutti i sottomoduli e aver aggiornato / estratto questi da remoto.
Questo, come sottolineato, è terminato
git submodule foreach git pull origin BRANCH
git submodule update
Scenario 2, che ritengo sia l'obiettivo del PO
Sono successe nuove cose in uno o più sottomoduli, e voglio 1) estrarre queste modifiche e 2) aggiornare il repository padre in modo da puntare al commit HEAD (più recente) di questo / questi sottomoduli.
Questo sarebbe fatto da
git submodule foreach git pull origin BRANCH
git add module_1_name
git add module_2_name
......
git add module_n_name
git push origin BRANCH
Non molto pratico, dal momento che dovresti hardcodificare n percorsi a tutti gli n sottomoduli in ad esempio uno script per aggiornare i puntatori di commit del repository principale.
Sarebbe bello avere un'iterazione automatica attraverso ogni sottomodulo, aggiornando il puntatore del repository padre (usando git add
) per puntare alla testa del sottomodulo / i.
Per questo, ho realizzato questo piccolo script Bash:
git-update-submodules.sh
#!/bin/bash
APP_PATH=$1
shift
if [ -z $APP_PATH ]; then
echo "Missing 1st argument: should be path to folder of a git repo";
exit 1;
fi
BRANCH=$1
shift
if [ -z $BRANCH ]; then
echo "Missing 2nd argument (branch name)";
exit 1;
fi
echo "Working in: $APP_PATH"
cd $APP_PATH
git checkout $BRANCH && git pull --ff origin $BRANCH
git submodule sync
git submodule init
git submodule update
git submodule foreach "(git checkout $BRANCH && git pull --ff origin $BRANCH && git push origin $BRANCH) || true"
for i in $(git submodule foreach --quiet 'echo $path')
do
echo "Adding $i to root repo"
git add "$i"
done
git commit -m "Updated $BRANCH branch of deployment repo to point to latest head of submodules"
git push origin $BRANCH
Per eseguirlo, esegui
git-update-submodules.sh /path/to/base/repo BRANCH_NAME
Elaborazione
Prima di tutto, presumo che il ramo con nome $ BRANCH (secondo argomento) esista in tutti i repository. Sentiti libero di renderlo ancora più complesso.
La prima coppia di sezioni sta verificando che gli argomenti siano presenti. Quindi estraggo le ultime cose del repository principale (preferisco usare --ff (avanzamento rapido) ogni volta che sto solo facendo pull. Ho rebase off, BTW).
git checkout $BRANCH && git pull --ff origin $BRANCH
Quindi potrebbe essere necessario inizializzare un sottomodulo, se sono stati aggiunti nuovi sottomoduli o non sono ancora inizializzati:
git submodule sync
git submodule init
git submodule update
Quindi aggiorno / estraggo tutti i sottomoduli:
git submodule foreach "(git checkout $BRANCH && git pull --ff origin $BRANCH && git push origin $BRANCH) || true"
Nota alcune cose: prima di tutto, sto concatenando alcuni comandi Git usando &&
- il che significa che il comando precedente deve essere eseguito senza errori.
Dopo un possibile pull riuscito (se sul telecomando sono state trovate nuove cose), faccio una spinta per garantire che un possibile merge-commit non venga lasciato indietro sul client. Ancora una volta, succede solo se un pull ha effettivamente portato nuove cose.
Infine, il finale || true
sta assicurando che lo script continui sugli errori. Per far funzionare tutto ciò, nell'iterazione deve essere racchiuso tra virgolette doppie e i comandi Git sono racchiusi tra parentesi (precedenza dell'operatore).
La mia parte preferita:
for i in $(git submodule foreach --quiet 'echo $path')
do
echo "Adding $i to root repo"
git add "$i"
done
Iterare tutti i sottomoduli - con --quiet
, che rimuove l'output 'Entering MODULE_PATH'. Utilizzando 'echo $path'
(deve essere tra virgolette singole), il percorso del sottomodulo viene scritto nell'output.
Questo elenco di percorsi relativi al sottomodulo viene acquisito in un array ( $(...)
) - infine iterare questo e fare git add $i
per aggiornare il repository padre.
Infine, un commit con qualche messaggio che spiega che il repository padre è stato aggiornato. Questo commit verrà ignorato per impostazione predefinita, se non è stato fatto nulla. Spingilo all'origine e il gioco è fatto.
Ho uno script che esegue questo in un lavoro Jenkins che in seguito si collega a una distribuzione automatica pianificata e funziona come un incantesimo.
Spero che questo possa essere d'aiuto a qualcuno.
--remote
opzione, forse sarebbe utile contrassegnarlo come la risposta accettata piuttosto che l'approccio "a mano" nella risposta di Jason?