1116 votes

Mise à jour du sous-module Git avec le dernier commit sur origin

J'ai un projet avec un sous-module Git. Il provient d'une URL ssh://... et est sur le commit A. Le commit B a été poussé vers cette URL, et je veux que le submodule récupère le commit, et le modifie.

Maintenant, je comprends que git submodule update devrait faire cela, 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 recherche (mais ce n'est pas possible, car il n'y a pas de demande de mot de passe ! git log et git show nier l'existence de nouveaux commits. Jusqu'à présent, je n'ai fait que rm -supprimer le module et le réinsérer, mais c'est à la fois faux en principe et fastidieux en pratique.

8 votes

La réponse de David Z. semble être la meilleure façon de procéder - maintenant que Git a intégré la fonctionnalité dont vous avez besoin via l'option --remote Peut-être serait-il utile de marquer cette option comme étant la réponse acceptée plutôt que l'approche "à la main" de la réponse de Jason ?

3 votes

Je suis tout à fait d'accord avec @MarkAmery. Bien que Jason ait donné une solution fonctionnelle, ce n'est pas la façon prévue de le faire, car elle laisse le pointeur de commit du sous-module au mauvais identifiant de commit. Le nouveau --remote est définitivement une meilleure solution à ce stade, et puisque cette question a été liée à un Gist Github sur les submodules, je pense qu'il serait préférable pour les lecteurs entrants de voir la nouvelle réponse.

0 votes

Belle touche avec le hunter2 mot de passe :o)

1837voto

Jason Points 3139

Le site git submodule update indique en fait à Git que vous voulez que vos sous-modules vérifient chacun le commit déjà spécifié dans l'index du superprojet. Si vous voulez mise à jour vos submodules à la dernière livraison disponible à partir de leur télécommande, vous devrez le faire directement dans les submodules.

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

416 votes

git submodule foreach git pull

3 votes

@MathiasBynens : Vous devez extraire une branche sur le submodule puisqu'il est dans un état détaché par défaut.

104 votes

@Nicklas Dans ce cas, utilisez git submodule foreach git pull origin master .

656voto

David Z Points 49476

Git 1.8.2 comporte une nouvelle option, --remote qui permettra exactement ce comportement. Exécution de

git submodule update --remote --merge

va récupérer les derniers changements en amont dans chaque sous-module, les fusionner, et extraire la dernière révision du sous-module. Comme la documentation le met :

-- distant

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

Ceci est équivalent à l'exécution de git pull dans chaque sous-module, ce qui est généralement exactement ce que vous voulez.

6 votes

"équivalent à la course git pull dans chaque sous-module" Pour clarifier, il n'y a pas de différence (du point de vue de l'utilisateur) entre ta réponse et git submodule foreach git pull ?

3 votes

@Dennis il fait essentiellement la même chose, mais je ne suis pas sûr que la fonctionnalité est exactement les mêmes. Il peut y avoir quelques différences mineures que je ne connais pas, par exemple dans la façon dont les deux commandes répondent à certains paramètres de configuration.

9 votes

J'aimerais pouvoir voter 10 000 fois plus haut. Pourquoi cela n'apparaît-il nulle part dans la documentation de git ? C'est un énorme oubli.

164voto

pinux Points 426

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

git submodule update --init

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

git submodule update --init --recursive

Parfois, cela ne fonctionne toujours pas, parce que d'une manière ou d'une autre, vous avez des modifications locales dans le répertoire du sous-module local pendant que le sous-module est mis à jour.

La plupart du temps, le changement local n'est pas forcément celui que vous souhaitez engager. Cela peut être dû à la suppression d'un fichier dans votre sous-module, etc. Si c'est le cas, faites une réinitialisation dans le répertoire de votre sous-module local et dans le répertoire parent de votre projet, exécutez à nouveau :

git submodule update --init --recursive

7 votes

C'est la vraie réponse. puis-je le pousser vers mon dépôt distant d'une manière ou d'une autre ?

1 votes

Cela fonctionne pour les nouveaux sous-modules ! Je pouvais mettre à jour tous les autres mais le dossier des nouveaux submodules restait vide jusqu'à ce que je lance cette commande.

3 votes

Il n'extrait pas les changements pour les submodules existants.

88voto

Mark Longair Points 93104

Votre projet principal pointe vers un commit particulier auquel le sous-module devrait se trouver. git submodule update essaie de vérifier ce commit dans chaque sous-module qui a été initialisé. Le sous-module est vraiment un dépôt indépendant - créer simplement un nouveau commit dans le sous-module et le pousser n'est pas suffisant. 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 c'est le bout de master :

cd mod
git checkout master
git pull origin master

Maintenant, retournez au projet principal, mettez en scène le sous-module et commettez-le :

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

Maintenant, poussez 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, en supposant qu'il a été initialisé.

30voto

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

Scénario 1

En utilisant les pointeurs de mon dépôt parent vers les sous-modules, je veux vérifier le commit dans chaque sous-module vers lequel le dépôt parent pointe, éventuellement après avoir d'abord itéré à travers tous les sous-modules et les avoir mis à jour/tirés à distance.

Cela se fait, comme on l'a souligné, avec

git submodule foreach git pull origin BRANCH
git submodule update

Scénario 2, qui, je pense, est le but du PO.

De nouvelles choses se sont produites dans un ou plusieurs sous-modules, et je veux 1) récupérer ces changements et 2) mettre à jour le dépôt parent pour qu'il pointe vers le commit HEAD (le plus récent) de ce/ces sous-modules.

Pour ce faire, il faudrait

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 il faudrait coder en dur n chemins vers les n submodules dans par exemple un script pour mettre à jour les pointeurs de commit du dépôt parent.

Il serait intéressant d'avoir une itération automatisée à travers chaque sous-module, en mettant à jour le pointeur du dépôt parent (à l'aide de git add ) pour pointer vers la tête du (des) sous-module(s).

Pour cela, j'ai fait ce petit Bash script :

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 le lancer, exécutez

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

Élaboration

Tout d'abord, je suppose que la branche avec le nom $BRANCH (deuxième argument) existe dans tous les dépôts. N'hésitez pas à rendre cela encore plus complexe.

Les deux premières sections consistent à vérifier que les arguments sont présents. Ensuite, je tire les derniers éléments du dépôt parent (je préfère utiliser --ff (avance rapide) quand je ne fais que des tirages. J'ai désactivé rebase, BTW).

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

Ensuite, il peut être nécessaire d'initialiser certains sous-modules, 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 et retire tous les sous-modules :

git submodule foreach "(git checkout $BRANCH && git pull --ff origin $BRANCH && git push origin $BRANCH) || true"

Remarquez certaines choses : Tout d'abord, j'enchaîne quelques commandes Git en utilisant && - ce qui signifie que la commande précédente doit s'exécuter sans erreur.

Après un éventuel pull réussi (si de nouveaux éléments ont été trouvés sur le distant), je fais un push pour m'assurer qu'un éventuel merge-commit n'est pas laissé sur le client. Encore une fois, cela n'arrive que si une traction a effectivement apporté de nouvelles choses.

Enfin, la finale || true est de s'assurer que script continue sur les erreurs. Pour que cela fonctionne, tout ce qui se trouve dans l'itération doit être enveloppé dans les guillemets et les commandes Git sont enveloppées dans les parenthèses (préséance 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érer tous les submodules - avec --quiet ce qui supprime le message "Entering MODULE_PATH". Utilisation de 'echo $path' (doit être entre guillemets), le chemin vers le sous-module est écrit en sortie.

Cette liste de chemins relatifs de sous-modules est capturée dans un tableau ( $(...) ) - enfin itérer ceci et faire 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-le vers l'origine, et vous avez terminé.

J'ai un script qui exécute cela dans un Jenkins qui s'enchaîne à un déploiement automatique programmé par la suite, et cela fonctionne comme un charme.

J'espère que cela pourra être utile à quelqu'un.

2 votes

!@#$% SO Nous utilisons des scripts similaires aux vôtres ; une remarque : au lieu de " git submodule foreach --quiet 'echo $path' ", nous utilisons " git submodule foreach --recursive --quiet pwd " à l'intérieur des boucles for. Le site pwd imprime le 'chemin absolu' approprié pour chaque sous-module présent ; --recursive s'assure que l'on visite tous submodules, y compris les submodules-within-submodules-... qui peuvent être présents dans un grand projet. Les deux méthodes causent des problèmes avec les répertoires qui incluent des espaces, par ex. /c/Users/Ger/Project\ Files/... La politique consiste donc à jamais utiliser les espaces blancs partout dans nos projets.

2 votes

C'est bien, et vous avez raison de dire qu'il y a un malentendu dans certaines réponses sur ce qu'est même la question, mais comme le souligne l'excellente réponse de David Z, votre script est inutile puisque la fonctionnalité a été intégrée dans Git depuis mi-2013 quand ils ont ajouté le. --remote option. git submodule update --remote se comporte approximativement de la même manière que votre script.

0 votes

@GerHobbelt Merci. Vous avez raison, nous n'avons qu'un seul niveau de sous-modules, donc je n'ai jamais pensé à le rendre récursif. Je ne mettrai pas à jour le script, avant d'avoir eu la chance de vérifier qu'il fonctionne comme prévu, mais définitivement mon script ingérerait les sous-sous-modules. En ce qui concerne les espaces dans les dossiers, cela ressemble définitivement à quelque chose à éviter ! :S

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X