web-dev-qa-db-fra.com

Comment faire des sous-modules git peu profonds?

Est-il possible d'avoir des sous-modules peu profonds? J'ai un superprojet avec plusieurs sous-modules, chacun avec une longue histoire, donc ça devient inutilement gros en traînant toute cette histoire.

Tout ce que j'ai trouvé est ce fil sans réponse .

Dois-je juste pirater git-submodule pour implémenter cela?

124
Mauricio Scheffer

Nouveau dans le prochain git1.8.4 (juillet 2013) :

"git submodule update" Peut éventuellement cloner les référentiels de sous-modules de manière superficielle.

(Et git 2.10 Q3 2016 permet d'enregistrer cela avec git config -f .gitmodules submodule.<name>.shallow true.
Voir la fin de cette réponse)

Voir commit 275cd184d52b5b81cb89e4ec33e540fb2ae61c1f :

Ajoutez l'option --depth Aux commandes d'ajout et de mise à jour de "git submodule", qui est ensuite transmise à la commande clone. Ceci est utile lorsque le ou les sous-modules sont énormes et que vous n'êtes vraiment intéressé par rien d'autre que le dernier commit.

Des tests sont ajoutés et certains ajustements d'indentation ont été effectués pour se conformer au reste du fichier de test sur "la mise à jour du sous-module peut gérer les liens symboliques dans pwd".

Signé par: Fredrik Gustafsson <[email protected]>
Acquitté par: Jens Lehmann <[email protected]>

Cela signifie que cela fonctionne:

git submodule add --depth 1 -- repository path
git submodule update --depth -- [<path>...]

Avec:

--depth::

Cette option est valide pour les commandes add et update.
Créez un clone "superficiel" avec un historique tronqué au nombre spécifié de révisions.


atwyman ajoute dans les commentaires :

Pour autant que je sache, cette option n'est pas utilisable pour les sous-modules qui ne suivent pas de très près master. Si vous définissez la profondeur 1, submodule update Ne pourra réussir que si le commit de sous-module souhaité est le dernier maître. Sinon, vous obtenez "fatal: reference is not a tree" .

C'est vrai.
Autrement dit, jusqu'au git 2.8 (mars 2016). Avec 2.8, le submodule update --depth A une chance de plus de réussir, même si le SHA1 est directement accessible à partir de l'une des têtes de repo distantes.

Voir commit fb43e31 (24 février 2016) par Stefan Beller (stefanbeller) .
Aidé par: Junio ​​C Hamano (gitster) .
(Fusionné par Junio ​​C Hamano - gitster - in commit 9671a76 , 26 février 2016)

sous-module: essayez plus fort de récupérer sha1 nécessaire en récupérant directement sha1

Lors de la révision d'une modification qui met également à jour un sous-module dans Gerrit, une pratique de révision courante consiste à télécharger et à sélectionner le correctif localement pour le tester.
Cependant, lors du test local, le 'git submodule update' Peut échouer lors de la récupération du sous-module correct sha1 car la validation correspondante dans le sous-module ne fait pas encore partie de l'historique du projet, mais aussi juste une modification proposée.

Si $sha1 Ne faisait pas partie de la récupération par défaut, nous essayons de récupérer le $sha1 Directement . Cependant, certains serveurs ne prennent pas en charge l'extraction directe par sha1, ce qui conduit à git-fetch À échouer rapidement.
Nous pouvons nous échouer ici car le sha1 encore manquant entraînerait de toute façon un échec plus tard dans la phase de paiement, donc échouer ici est aussi bon que possible.


MVG souligne dans les commentaires à commit fb43e31 (git 2.9, févr.2016)

Il me semble que commit fb43e31 demande le commit manquant par l'identifiant SHA1, donc les paramètres uploadpack.allowReachableSHA1InWant Et uploadpack.allowTipSHA1InWant Sur le serveur affecteront probablement si cela fonctionne.
J'ai écrit un post à la liste git aujourd'hui , soulignant comment l'utilisation de sous-modules peu profonds pourrait être améliorée pour fonctionner dans certains scénarios, à savoir si la validation est également une balise.
Attendons voir.

Je suppose que c'est une raison pour laquelle fb43e31 a fait de la récupération pour un SHA1 spécifique une solution de rechange après la récupération pour la branche par défaut.
Néanmoins, dans le cas de "--depth 1", je pense qu'il serait logique d'interrompre tôt: si aucune des références répertoriées ne correspond à celle demandée, et que la demande par SHA1 n'est pas prise en charge par le serveur, il est inutile de récupérer quoi que ce soit, car nous ne serons pas en mesure de satisfaire aux exigences du sous-module de toute façon.


Mise à jour août 2016 (3 ans plus tard)

Avec Git 2.10 (Q3 2016), vous pourrez faire

 git config -f .gitmodules submodule.<name>.shallow true

Voir " sous-module Git sans poids supplémentaire " pour en savoir plus.


Git 2.13 (Q2 2017) ajoute commit 8d3047c (19 avril 2017) par Sebastian Schuberth (sschuberth) .
(Fusionné par Sebastian Schuberth - sschuberth - in commit 8d3047c , 20 avril 2017)

un clone de ce sous-module sera exécuté comme un clone peu profond (avec une profondeur d'historique de 1)

Cependant, Ciro Santilli ajoute dans les commentaires (et les détails dans sa réponse )

shallow = true Sur .gitmodules Affecte uniquement la référence suivie par le HEAD de la télécommande lors de l'utilisation de --recurse-submodules, Même si la validation cible est pointée vers par une branche, et même si vous mettez également branch = mybranch sur .gitmodules.


Git 2.20 (Q4 2018) améliore la prise en charge du sous-module, qui a été mis à jour pour lire à partir du blob à HEAD:.gitmodules Lorsque le fichier .gitmodules Est manquant dans l'arborescence de travail.

Voir commit 2b1257e , commit 76e9bdc (25 octobre 2018) et commit b5c259f , commit 23dd8f5 , commit b2faad4 , commit 2502ffc , commit 996df4d , commit d1b13df , commit 45f5ef , commit bcbc78 (05 octobre 2018) par Antonio Ospite (ao2) .
(Fusionné par Junio ​​C Hamano - gitster - in commit abb4824 , 13 novembre 2018)

submodule: supporte la lecture de .gitmodules quand il n'est pas dans l'arborescence de travail

Lorsque le fichier .gitmodules N'est pas disponible dans l'arborescence de travail, essayez d'utiliser le contenu de l'index et de la branche actuelle.
Cela couvre le cas où le fichier fait partie du référentiel mais pour une raison quelconque, il n'est pas extrait, par exemple en raison d'une extraction clairsemée.

Cela permet d'utiliser au moins les commandes 'git submodule' Qui lire le fichier de configuration gitmodules sans remplir complètement l'arborescence de travail.

Pour écrire dans .gitmodules, Il faudra toujours extraire le fichier, alors vérifiez-le avant d'appeler config_set_in_gitmodules_file_gently.

Ajoutez une vérification similaire également dans git-submodule.sh::cmd_add() pour anticiper l'échec éventuel de la commande "git submodule add" Lorsque .gitmodules N'est pas accessible en écriture; cela empêche la commande de laisser le référentiel dans un état parasite (par exemple, le référentiel de sous-module a été cloné mais .gitmodules n'a pas été mis à jour car config_set_in_gitmodules_file_gently a échoué).

De plus, étant donné que config_from_gitmodules() accède désormais au magasin d'objets global, il est nécessaire de protéger tous les chemins de code qui appellent la fonction contre l'accès simultané au magasin d'objets global.
Actuellement, cela ne se produit que dans builtin/grep.c::grep_submodules(), donc appelez grep_read_lock() avant d'appeler du code impliquant config_from_gitmodules().

REMARQUE: il existe un cas rare où cette nouvelle fonctionnalité ne fonctionne pas encore correctement: les sous-modules imbriqués sans .gitmodules Dans leur arborescence de travail.

114
VonC

Git 2.9. supporte directement les sous-modules clones superficiels, donc maintenant vous pouvez simplement appeler:

git clone url://to/source/repository --recursive --shallow-submodules
22
KindDragon

Suivant réponse de Ryan J'ai pu trouver ce script simple qui itère à travers tous les sous-modules et les clone peu profonds:

#!/bin/bash
git submodule init
for i in $(git submodule | sed -e 's/.* //'); do
    spath=$(git config -f .gitmodules --get submodule.$i.path)
    surl=$(git config -f .gitmodules --get submodule.$i.url)
    git clone --depth 1 $surl $spath
done
git submodule update
15
Mauricio Scheffer

En lisant le sous-module git "source", on dirait git submodule add peut gérer des sous-modules dont les référentiels sont déjà présents. Dans ce cas...

$ git clone $remote1 $repo
$ cd $repo
$ git clone --depth 5 $remotesub1 $sub1
$ git submodule add $remotesub1 $sub1
#repeat as necessary...

Vous voudrez vous assurer que la validation requise se trouve dans le référentiel de sous-module, alors assurez-vous de définir une profondeur appropriée.

Edit: Vous pourrez peut-être vous en sortir avec plusieurs clones de sous-modules manuels suivis d'une seule mise à jour:

$ git clone $remote1 $repo
$ cd $repo
$ git clone --depth 5 $remotesub1 $sub1
#repeat as necessary...
$ git submodule update
8
Ryan Graham

Résumé du comportement buggé/inattendu/ennuyeux de Git 2.14.1

  1. shallow = true dans .gitmodules affecte uniquement git clone --recurse-submodules si le HEAD du sous-module distant pointe vers la validation requise, même si la validation cible est pointée par une branche, et même si vous mettez branch = mybranch sur le .gitmodules ainsi que.

    Script de test local . Même comportement sur GitHub 2017-11, où HEAD est contrôlé par le paramètre de dépôt de branche par défaut:

    git clone --recurse-submodules https://github.com/cirosantilli/test-shallow-submodule-top-branch-shallow
    cd test-shallow-submodule-top-branch-shallow/mod
    git log
    # Multiple commits, not shallow.
    
  2. git clone --recurse-submodules --shallow-submodules échoue si la validation n'est pas référencée par une branche ou une balise avec un message: error: Server does not allow request for unadvertised object.

    Script de test local . Même comportement sur GitHub:

    git clone --recurse-submodules --shallow-submodules https://github.com/cirosantilli/test-shallow-submodule-top-sha
    # error
    

    J'ai également demandé sur la liste de diffusion: https://marc.info/?l=git&m=151863590026582&w=2 et la réponse était:

    En théorie, cela devrait être facile. :)

    En pratique, pas tellement, malheureusement. En effet, le clonage n'obtiendra que la dernière astuce d'une branche (généralement master). Il n'y a pas de mécanisme dans le clone pour spécifier le sha1 exact souhaité.

    Le protocole de fil prend en charge la demande de sha1s exacts, de sorte que cela devrait être couvert. (Avertissement: cela ne fonctionne que si l'opérateur du serveur active uploadpack.allowReachableSHA1InWant quel github n'a pas AFAICT)

    git-fetch permet de récupérer arbitrairement sha1, donc comme solution de contournement, vous pouvez exécuter une récupération après le clone récursif en utilisant "git submodule update" car cela utilisera des récupérations après le clone initial.

Test TODO: allowReachableSHA1InWant .

Les emplacements canoniques de vos sous-modules sont-ils éloignés? Si oui, êtes-vous d'accord pour les cloner une fois? En d'autres termes, voulez-vous les clones peu profonds simplement parce que vous souffrez de la bande passante gaspillée des (ré) clones de sous-module fréquents?

Si vous voulez des clones peu profonds pour économiser l'espace disque local, la réponse de Ryan Graham semble être une bonne façon de procéder. Clonez manuellement les référentiels afin qu'ils soient peu profonds. Si vous pensez que ce serait utile, adaptez git submodule pour le soutenir. Envoyez un email à la liste pour en savoir plus (conseils pour sa mise en œuvre, suggestions sur l'interface, etc.). À mon avis, les gens là-bas sont assez favorables aux contributeurs potentiels qui souhaitent sérieusement améliorer Git de manière constructive.

Si vous êtes d'accord pour faire un clone complet de chaque sous-module (plus des récupérations ultérieures pour les garder à jour), vous pouvez essayer d'utiliser le --reference option de git submodule update (c'est dans Git 1.6.4 et versions ultérieures) pour faire référence aux magasins d'objets locaux (par exemple make --mirror clones des référentiels de sous-modules canoniques, puis utilisez --reference dans vos sous-modules pour pointer vers ces clones locaux). N'oubliez pas de lire à propos de git clone --reference/git clone --shared avant d'utiliser --reference. Le seul problème probable avec les miroirs de référencement serait s'ils finissaient par récupérer les mises à jour non à avance rapide (bien que vous puissiez activer les reflogs et étendre leurs fenêtres d'expiration pour aider à conserver les validations abandonnées qui pourraient causer un problème). Vous ne devriez avoir aucun problème tant que

  • vous n'effectuez aucun commit de sous-module local, ou
  • les validations laissées pendantes par des avances non rapides que les référentiels canoniques pourraient publier ne sont pas des ancêtres des validations de votre sous-module local, ou
  • vous vous efforcez de conserver vos validations de sous-module local rebasées par-dessus tout ce que les avances non rapides peuvent être publiées dans les référentiels canoniques de sous-module.

Si vous optez pour quelque chose comme ça et qu'il y a une chance que vous portiez des commits de sous-modules locaux dans vos arborescences de travail, ce serait probablement une bonne idée de créer un système automatisé qui s'assure que les objets critiques référencés par les sous-modules extraits ne sont pas laissé pendant dans les référentiels miroirs (et s'il en existe, copiez-les dans les référentiels qui en ont besoin).

Et, comme le git clone la page de manuel dit, n'utilisez pas --reference si vous ne comprenez pas ces implications.

# Full clone (mirror), done once.
git clone --mirror $sub1_url $path_to_mirrors/$sub1_name.git
git clone --mirror $sub2_url $path_to_mirrors/$sub2_name.git

# Reference the full clones any time you initialize a submodule
git clone $super_url super
cd super
git submodule update --init --reference $path_to_mirrors/$sub1_name.git $sub1_path_in_super
git submodule update --init --reference $path_to_mirrors/$sub2_name.git $sub2_path_in_super

# To avoid extra packs in each of the superprojects' submodules,
#   update the mirror clones before any pull/merge in super-projects.
for p in $path_to_mirrors/*.git; do GIT_DIR="$p" git fetch; done

cd super
git pull             # merges in new versions of submodules
git submodule update # update sub refs, checkout new versions,
                     #   but no download since they reference the updated mirrors

Alternativement, au lieu de --reference, vous pouvez utiliser les clones miroirs en combinaison avec la fonctionnalité de liaison fixe par défaut de git clone en utilisant des miroirs locaux comme source pour vos sous-modules. Dans les nouveaux clones de super-projets, faites git submodule init, modifiez les URL des sous-modules dans .git/config pour pointer vers les miroirs locaux, puis faites git submodule update. Vous devez recloner tous les sous-modules récupérés existants pour obtenir les liens physiques. Vous économiseriez de la bande passante en téléchargeant une seule fois dans les miroirs, puis en récupérant localement ceux-ci dans vos sous-modules extraits. La liaison matérielle permettrait d'économiser de l'espace disque (bien que les récupérations aient tendance à s'accumuler et à être dupliquées sur plusieurs instances des magasins d'objets des sous-modules extraits; vous pouvez périodiquement recloner les sous-modules extraits des miroirs pour regagner l'espace disque fourni par hardlinking).

2
Chris Johnsen

Référence à Comment cloner le dépôt git avec une révision/un ensemble de modifications spécifique?

J'ai écrit un script simple qui n'a aucun problème lorsque votre référence de sous-module est loin du maître

git submodule foreach --recursive 'git rev-parse HEAD | xargs -I {} git fetch Origin {} && git reset --hard FETCH_HEAD'

Cette instruction récupérera la version référencée du sous-module.

C'est rapide mais vous ne pouvez pas valider votre modification sur le sous-module (vous devez le récupérer avant de le modifier https://stackoverflow.com/a/17937889/3156509 )

en entier:

#!/bin/bash
git submodule init
git submodule foreach --recursive 'git rev-parse HEAD | xargs -I {} git fetch Origin {} && git reset --hard FETCH_HEAD'
git submodule update --recursive
1
Beeno Tung

Le clone peu profond d'un sous-module est parfait car il crée un instantané à une révision/un ensemble de modifications particulier. Il est facile de télécharger un Zip à partir du site Web, j'ai donc essayé un script.

#!/bin/bash
git submodule deinit --all -f
for value in $(git submodule | Perl -pe 's/.*(\w{40})\s([^\s]+).*/\1:\2/'); do
  mysha=${value%:*}
  mysub=${value#*:}
  myurl=$(grep -A2 -Pi "path = $mysub" .gitmodules | grep -Pio '(?<=url =).*/[^.]+')
  mydir=$(dirname $mysub)
  wget $myurl/archive/$mysha.Zip
  unzip $mysha.Zip -d $mydir
  test -d $mysub && rm -rf $mysub
  mv $mydir/*-$mysha $mysub
  rm $mysha.Zip
done
git submodule init

git submodule deinit --all -f efface l'arborescence des sous-modules qui permet au script d'être réutilisable.

git submodule récupère les 40 caractères sha1 suivis d'un chemin qui correspond à la même chose dans .gitmodules. J'utilise Perl pour concaténer ces informations, délimitées par deux points, puis j'utilise une transformation de variable pour séparer les valeurs en mysha et mysub.

Ce sont les clés critiques car nous avons besoin du sha1 pour télécharger et du chemin pour corréler le url dans .gitmodules.

Étant donné une entrée de sous-module typique:

[submodule "label"]
    path = localpath
    url = https://github.com/repository.git

myurl touches sur path = regarde ensuite 2 lignes après pour obtenir la valeur. Cette méthode peut ne pas fonctionner de manière cohérente et nécessiter un raffinement. L'URL grep supprime tout .git saisissez les références en les faisant correspondre à la dernière / et tout ce qui peut aller jusqu'à ..

mydir est mysub moins une finale /name qui serait par le répertoire menant au nom du sous-module.

Vient ensuite un wget au format de l'URL de l'archive Zip téléchargeable. Cela pourrait changer à l'avenir.

Décompressez le fichier dans mydir qui serait le sous-répertoire spécifié dans le chemin du sous-module. Le dossier résultant sera le dernier élément du url-sha1.

Vérifiez si le sous-répertoire spécifié dans le chemin du sous-module existe et supprimez-le pour permettre de renommer le dossier extrait.

mv renommez le dossier extrait contenant notre sha1 en son chemin de sous-module correct.

Supprimer le fichier Zip téléchargé.

Init du sous-module

Il s'agit plus d'une preuve de concept WIP que d'une solution. Lorsque cela fonctionne, le résultat est un clone superficiel d'un sous-module à un ensemble de modifications spécifié.

Si le référentiel réintroduit un sous-module dans un autre commit, réexécutez le script pour le mettre à jour.

La seule fois où un script comme celui-ci serait utile est pour la construction locale non collaborative d'un projet source.

1
noabody

J'ai créé une version légèrement différente, car lorsqu'elle ne fonctionne pas sur le bord de saignement, ce que ne font pas tous les projets. Les ajouts de sous-modules standard n'ont pas fonctionné ni le script ci-dessus. J'ai donc ajouté une recherche de hachage pour la référence de la balise, et si elle n'en a pas, elle revient au clone complet.

#!/bin/bash
git submodule init
git submodule | while read hash name junk; do
    spath=$(git config -f .gitmodules --get submodule.$name.path)
    surl=$(git config -f .gitmodules --get submodule.$name.url)
    sbr=$(git ls-remote --tags $surl | sed -r "/${hash:1}/ s|^.*tags/([^^]+).*\$|\1|p;d")
    if [ -z $sbr ]; then
        git clone $surl $spath
    else
        git clone -b $sbr --depth 1 --single-branch $surl $spath
    fi
done
git submodule update 
1
sfossen