web-dev-qa-db-fra.com

Mettre à jour le sous-module Git avec le dernier commit sur l'origine

J'ai un projet avec un sous-module Git. Il provient d'une URL ssh: // ... et se trouve sur la validation A. La validation B a été poussée vers cette URL et je souhaite que le sous-module récupère la validation et la modifie.

D'après ce que j'ai compris, git submodule update devrait le faire, mais ce n'est pas le cas. Il ne fait rien (pas de sortie, code de sortie de succès). Voici un exemple:

$ mkdir foo
$ cd foo
$ git init .
Initialized empty Git repository in /.../foo/.git/
$ git submodule add ssh://user@Host/git/mod mod
Cloning into mod...
user@Host's password: hunter2
remote: Counting objects: 131, done.
remote: Compressing objects: 100% (115/115), done.
remote: Total 131 (delta 54), reused 0 (delta 0)
Receiving objects: 100% (131/131), 16.16 KiB, done.
Resolving deltas: 100% (54/54), done.
$ git commit -m "Hello world."
[master (root-commit) 565b235] Hello world.
 2 files changed, 4 insertions(+), 0 deletions(-)
 create mode 100644 .gitmodules
 create mode 160000 mod
# At this point, ssh://user@Host/git/mod changes; submodule needs to change too.
$ git submodule init
Submodule 'mod' (ssh://user@Host/git/mod) registered for path 'mod'
$ git submodule update
$ git submodule sync
Synchronizing submodule url for 'mod'
$ git submodule update
$ man git-submodule 
$ git submodule update --rebase
$ git submodule update
$ echo $?
0
$ git status
# On branch master
nothing to commit (working directory clean)
$ git submodule update mod
$ ...

J'ai aussi essayé git fetch mod, qui semble effectuer une extraction (mais ne peut pas, car il ne demande pas de mot de passe!), Mais git log et git show nient l'existence de nouveaux commits. Jusqu'ici, je viens juste de rm- ajouter le module et de le rajouter, mais ceci est à la fois faux en principe et fastidieux en pratique.

756
Thanatos

La commande git submodule update indique en fait à Git que vous souhaitez que vos sous-modules extraient à chaque extraction la validation déjà spécifiée dans l'index du superprojet. Si vous souhaitez mettre à jour vos sous-modules avec la dernière validation disponible depuis leur télécommande, vous devrez le faire directement dans les sous-modules.

Donc en résumé:

_# Get the submodule initially
git submodule add ssh://bla submodule_dir
git submodule init

# Time passes, submodule upstream is updated
# and you now want to update

# Change to the submodule directory
cd submodule_dir

# Checkout desired branch
git checkout master

# Update
git pull

# Get back to your project root
cd ..

# Now the submodules are in the state you want, so
git commit -am "Pulled down update to submodule_dir"
_

Ou, si vous êtes une personne occupée:

_git submodule foreach git pull Origin master
_
1300
Jason

Git 1.8.2 propose une nouvelle option, --remote, qui activera exactement ce comportement. Fonctionnement

git submodule update --remote --merge

récupérera les dernières modifications en amont dans chaque sous-module, les fusionnera et extraira la dernière révision du sous-module. Comme la documentation le dit:

--éloigné

Cette option n'est valide que pour la commande de mise à jour. Au lieu d'utiliser le SHA-1 enregistré du superprojet pour mettre à jour le sous-module, utilisez le statut de la branche de suivi à distance du sous-module.

Cela équivaut à exécuter git pull dans chaque sous-module, ce qui correspond généralement exactement à ce que vous souhaitez.

395
David Z

Dans le répertoire parent de votre projet, exécutez:

git submodule update --init

Ou si vous avez des sous-modules récursifs exécutez:

git submodule update --init --recursive

Parfois, cela ne fonctionne toujours pas, car vous avez des modifications locales dans le répertoire du sous-module local pendant la mise à jour du sous-module.

La plupart du temps, le changement local peut ne pas être celui que vous souhaitez valider. Cela peut arriver à cause d'une suppression de fichier dans votre sous-module, etc. Si c'est le cas, effectuez une réinitialisation dans votre répertoire de sous-module local et dans le répertoire parent de votre projet, exécutez à nouveau:

git submodule update --init --recursive
110
pinux

Votre projet principal pointe vers un commit particulier dans lequel le sous-module devrait être. git submodule update essaie de vérifier cette validation dans chaque sous-module initialisé. Le sous-module est vraiment un référentiel indépendant - il suffit de créer un nouveau commit dans le sous-module et de pousser cela ne suffit pas. Vous devez également ajouter explicitement la nouvelle version du sous-module dans le projet principal.

Donc, dans votre cas, vous devriez trouver le bon commit dans le sous-module - supposons que ce soit la pointe de master:

cd mod
git checkout master
git pull Origin master

Revenons maintenant au projet principal, montez le sous-module et engagez-le:

cd ..
git add mod
git commit -m "Updating the submodule 'mod' to the latest version"

Poussez maintenant votre nouvelle version du projet principal:

git Push Origin master

À partir de ce moment, si quelqu'un d'autre met à jour son projet principal, alors git submodule update pour eux mettra à jour le sous-module, à condition qu'il ait été initialisé.

71
Mark Longair

Il semble que deux scénarios différents soient mélangés dans cette discussion:

Scénario 1

En utilisant les pointeurs vers les sous-modules de mon référentiel parent, je souhaite extraire la validation de chaque sous-module vers lequel pointe le référentiel parent, éventuellement après avoir d'abord parcouru tous les sous-modules et les avoir mis à jour/extraits à distance.

Ceci est, comme indiqué, fait avec

git submodule foreach git pull Origin BRANCH
git submodule update

Scénario 2, que je pense est ce que l'OP vise à

De nouveaux éléments sont apparus dans un ou plusieurs sous-modules et je souhaite 1) extraire ces modifications et 2) mettre à jour le référentiel parent pour qu'il pointe vers le commit HEAD (dernier) de ce/ces sous-modules.

Cela serait fait par

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

Pas très pratique, car vous auriez à coder en dur n chemins vers tous les n sous-modules, par exemple. un script pour mettre à jour les pointeurs de validation du référentiel parent.

Il serait intéressant d’effectuer une itération automatisée dans chaque sous-module, en mettant à jour le pointeur du référentiel parent (à l’aide de git add) pour qu'il pointe vers la tête du ou des sous-modules.

Pour cela, j'ai réalisé ce petit 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

Pour l'exécuter, exécutez

git-update-submodules.sh /path/to/base/repo BRANCH_NAME

Elaboration

Tout d'abord, je suppose que la branche portant le nom $ BRANCH (second argument) existe dans tous les référentiels. N'hésitez pas à rendre cela encore plus complexe.

Les deux premières sections consistent à vérifier que les arguments sont bien présents. Ensuite, je tire les derniers éléments du référentiel parent (je préfère utiliser --ff (avance rapide) chaque fois que je fais juste des pulls. J'ai rebase off, BTW).

git checkout $BRANCH && git pull --ff Origin $BRANCH

Ensuite, une initialisation de sous-module peut être nécessaire si de nouveaux sous-modules ont été ajoutés ou ne sont pas encore initialisés:

git submodule sync
git submodule init
git submodule update

Ensuite, je mets à jour/tire tous les sous-modules:

git submodule foreach "(git checkout $BRANCH && git pull --ff Origin $BRANCH && git Push Origin $BRANCH) || true"

Notez quelques points: Tout d’abord, j’enchaîne des commandes Git en utilisant && - ce qui signifie que la commande précédente doit être exécutée sans erreur.

Après un possible tirage réussi (si de nouveaux éléments ont été trouvés sur la télécommande), je fais un Push pour m'assurer qu'un éventuel commit de fusion n'est pas laissé sur le client. Encore une fois, cela ne se produit que si un pull a réellement apporté de nouvelles choses.

Enfin, le dernier || true garantit que le script continue en cas d'erreur. Pour que cela fonctionne, tout ce qui se trouve dans l'itération doit être placé entre guillemets et les commandes Git entre parenthèses (priorité des opérateurs).

Ma partie préférée:

for i in $(git submodule foreach --quiet 'echo $path')
do
  echo "Adding $i to root repo"
  git add "$i"
done

Itérez tous les sous-modules - avec --quiet, qui supprime la sortie 'Entering MODULE_PATH'. En utilisant 'echo $path' (doit être entre guillemets simples), le chemin du sous-module est écrit dans la sortie.

Cette liste de chemins de sous-modules relatifs est capturée dans un tableau ($(...)). Finalement, répétez cette opération et exécutez git add $i pour mettre à jour le référentiel parent.

Enfin, un commit avec un message expliquant que le référentiel parent a été mis à jour. Ce commit sera ignoré par défaut si rien n'a été fait. Poussez ceci vers l'origine, et vous avez terminé.

J'ai un script qui l'exécute dans un travail Jenkins qui est ensuite lié à un déploiement automatisé planifié, et il fonctionne à merveille.

J'espère que cela aidera quelqu'un.

21

Simplement et simplement, aller chercher les sous-modules:

git submodule update --init --recursive

Et maintenant, procédez à leur mise à jour vers la dernière branche principale (par exemple):

git submodule foreach git pull Origin master
17
git pull --recurse-submodules

Cela va tirer tous les derniers commits.

4
user4016337

@ Jason est correct dans un sens mais pas entièrement.

mise à jour

Mettez à jour les sous-modules enregistrés, c’est-à-dire clonez les sous-modules manquants et extrayez le commit spécifié dans l’index du référentiel contenant. Ceci fera en sorte que les sous-modules HEAD seront détachés à moins que --rebase ou --merge ne soit spécifié ou que le sous-module de clé. $ Name.update ne soit défini à rebase ou à fusion.

Ainsi, git submodule update effectue l'extraction, mais il s'agit de la validation dans l'index du référentiel contenant. Il ne connaît pas encore le nouveau commit en amont. Allez donc dans votre sous-module, obtenez le commit que vous voulez et validez l’état du sous-module mis à jour dans le référentiel principal, puis faites le git submodule update.

3
manojlds

Dans mon cas, je souhaitais que git se mette à jour et recompile en même temps tous les fichiers manquants.

Ce qui suit a restauré les fichiers manquants (grâce à --force qui ne semble pas avoir été mentionné ici), mais il n'a pas généré de nouveau commit:

git submodule update --init --recursive --force

Cela a:

git submodule update --recursive --remote --merge --force

2
noseratio

Notez que la forme moderne de mise à jour des commits de sous-modules serait:

git submodule update --recursive --remote --merge --force

L'ancienne forme était:

git submodule foreach --quiet git pull --quiet Origin

Sauf que ... cette seconde forme n'est pas vraiment "silencieuse".

Voir commit a282f5a (12 avril 2019) par Nguyễn Thái Ngọc Duy (pclouds) .
(Fusionnée par Junio ​​C Hamano - gitster - dans commit f1c9f6c , 25 avril 2019)

submodule foreach: correction du non respect de "<command> --quiet"

Robin a rapporté que

git submodule foreach --quiet git pull --quiet Origin

n'est plus vraiment silencieux.
Il devrait être silencieux avant fc1b924 (submodule: port submodule sous-commande 'foreach' de Shell à C, 2018-05-10, Git v2.19.0-rc0) parce que parseopt ne peut pas manger accidentellement les options alors.

"git pull" se comporte comme si --quiet n'était pas donné.

Cela est dû au fait que parseopt dans submodule--helper essaiera d'analyser les deux options --quiet comme s'il s'agissait des options foreach et non de git-pull.
Les options analysées sont supprimées de la ligne de commande. Alors, quand on tire plus tard, on exécute juste ceci

git pull Origin

Lors de l'appel de l'aide du sous-module, l'ajout de "--" devant "git pull" arrêtera parseopt pour l'analyse des options n'appartenant pas à submodule--helper foreach.

PARSE_OPT_KEEP_UNKNOWN est supprimé par mesure de sécurité. parseopt ne devrait jamais voir d'options inconnues ou quelque chose s'est mal passé. Il y a aussi quelques mises à jour de chaîne d'utilisation pendant que je les regarde.

J'y ajoute aussi "--" aux autres sous-commandes qui passent "$@" à submodule--helper. "$@" dans ces cas sont des chemins et moins susceptibles d'être --something-like-this.
Mais le point reste vrai, git-submodule a analysé et classé quelles sont les options, quels sont les chemins.
submodule--helper ne devrait jamais considérer les chemins passés par git-submodule comme des options, même s'ils en ressemblent.


Et Git 2.23 (Q3 2019) corrige un autre problème: "git submodule foreach" ne protégeait pas les options de ligne de commande transmises à la commande à exécuter correctement dans chaque sous-module, lorsque l'option "--recursive" était utilisée.

Voir commit 30db18b (24 juin 2019) par Morian Sonnet (momoson) .
(Fusionné par Junio ​​C Hamano - gitster - dans commit 968eecb , 9 juillet 2019)

submodule foreach: corrige la récurrence des options

Appel:

git submodule foreach --recursive <subcommand> --<option>

conduit à une erreur indiquant que l'option --<option> est inconnue de submodule--helper.
Ce n'est bien sûr que lorsque <option> n'est pas une option valide pour git submodule foreach.

La raison en est que l'appel ci-dessus est traduit en interne en appel à submodule-helper:

git submodule--helper foreach --recursive \
    -- <subcommand> --<option>

Cet appel commence par exécuter la sous-commande avec son option dans le sous-module de premier niveau et continue par l'appel de la prochaine itération de l'appel submodule foreach

git --super-prefix <submodulepath> submodule--helper \
   foreach --recursive <subcommand> --<option>

dans le sous-module de premier niveau. Notez que le double tiret devant la sous-commande est manquant.

Ce problème ne commence à apparaître que récemment, l'indicateur PARSE_OPT_KEEP_UNKNOWN pour l'analyse des arguments de git submodule foreach a été supprimé de la validation a282f5a .
Par conséquent, l'option inconnue est maintenant mise en cause, l'analyse des arguments n'étant pas terminée correctement par le double tiret.

Ce commit corrige le problème en ajoutant le double tiret devant la sous-commande lors de la récursion.

2
VonC

Si vous ne connaissez pas la branche hôte, indiquez ceci:

git submodule foreach git pull Origin $(git rev-parse --abbrev-ref HEAD)

Il obtiendra une branche du référentiel principal Git puis, pour chaque sous-module, effectuera un tirage de la même branche.

1
NickUnuchek

Voici un one-liner génial pour tout mettre à jour avec la dernière version de master:

git submodule foreach 'git fetch Origin --tags; git checkout master; git pull' && git pull && git submodule update --init --recursive

Merci à Mark Jaquith

1
dustinrwh