Pourquoi mon submodule HEAD Git est-il détaché du maître?

163

J'utilise des sous-modules Git. Après avoir extrait les modifications du serveur, la tête de mon sous-module est souvent détachée de la branche principale.

Pourquoi cela arrive-t-il?

Je dois toujours faire:

git branch
git checkout master

Comment puis-je m'assurer que mon sous-module pointe toujours vers la branche principale?

om471987
la source
1
Avez-vous lu cette réponse? stackoverflow.com/questions/1777854/…
Johnny Z
@bitoiu J'ai regardé sous-arbre et Google Repo. Je n'ai pas encore de solution parfaite :(
om471987
1
mon expérience avec gitsubmodules, dans un environnement CI est horrible, peut-être que d'autres personnes ont de meilleures expériences.
bitoiu le
@JohnnyZ Merci. J'ai compris que le sous-module pointe vers un commit et non vers la tête de l'arbre. Mais pourquoi détaché de la branche. Si vous avez une branche, elle ne devrait pas y être attachée par défaut
om471987
3
Ne soyez pas trop rapide pour rejeter les sous-modules simplement parce que vous avez entendu dire qu'ils sont mauvais. Ils sont une solution médiocre si vous souhaitez une intégration continue, mais ils sont une solution presque parfaite si vous souhaitez intégrer du code à partir d'un projet externe et que vous gérez explicitement tous les pulls. C'est souvent la meilleure pratique si vous intégrez avec un module non-forké qui n'est pas contrôlé par votre organisation. Le problème est qu'ils sont une solution tentante dans toutes sortes d'autres situations où ils ne fonctionnent pas très bien du tout. Le meilleur conseil est de lire comment ils fonctionnent et d'évaluer votre scénario.
Sarah G

Réponses:

176

ÉDITER:

Voir @Simba Answer pour une solution valide

submodule.<name>.updateest ce que vous voulez changer, consultez la documentation - par défaut,checkout
submodule.<name>.branch spécifiez la branche distante à suivre - par défautmaster


ANCIENNE RÉPONSE:

Personnellement, je déteste les réponses ici qui dirigent vers des liens externes qui peuvent cesser de fonctionner avec le temps et vérifier ma réponse ici (à moins que la question ne soit dupliquée) - diriger vers une question qui couvre le sujet entre les lignes d'un autre sujet, mais globalement égale: "Je suis ne répond pas, lisez la documentation. "

Revenons donc à la question: pourquoi cela se produit-il?

Situation que vous avez décrite

Après avoir extrait les modifications du serveur, la tête de mon sous-module est souvent détachée de la branche principale.

C'est un cas courant lorsque l'on n'utilise pas trop souvent des sous-modules ou qu'on vient de commencer avec des sous-modules . Je crois avoir raison de dire que nous avons tous été là à un moment donné où la tête de notre sous-module se détache.

  • Cause: votre sous-module ne suit pas la branche correcte (maître par défaut).
    Solution: assurez-vous que votre sous-module suit la bonne branche
$ cd <submodule-path>
# if the master branch already exists locally:
# (From git docs - branch)
# -u <upstream>
# --set-upstream-to=<upstream>
#    Set up <branchname>'s tracking information so <upstream>
#    is considered <branchname>'s upstream branch.
#    If no <branchname> is specified, then it defaults to the current branch.
$ git branch -u <origin>/<branch> <branch>
# else:
$ git checkout -b <branch> --track <origin>/<branch>
  • Cause: votre référentiel parent n'est pas configuré pour suivre la branche des sous-modules.
    Solution: faites en sorte que votre sous-module suive sa branche distante en ajoutant de nouveaux sous-modules avec les deux commandes suivantes.
    • Dites d'abord à git de suivre votre télécommande <branch>.
    • vous dites à git d'effectuer un rebase ou une fusion au lieu de vérifier
    • vous dites à git de mettre à jour votre sous-module à distance.
    $ git submodule add -b <branch> <repository> [<submodule-path>]
    $ git config -f .gitmodules submodule.<submodule-path>.update rebase
    $ git submodule update --remote
  • Si vous n'avez pas ajouté votre sous-module existant comme celui-ci, vous pouvez facilement résoudre ce problème:
    • Vous voulez d'abord vous assurer que votre sous-module a extrait la branche dont vous souhaitez faire le suivi.
    $ cd <submodule-path>
    $ git checkout <branch>
    $ cd <parent-repo-path>
    # <submodule-path> is here path releative to parent repo root
    # without starting path separator
    $ git config -f .gitmodules submodule.<submodule-path>.branch <branch>
    $ git config -f .gitmodules submodule.<submodule-path>.update <rebase|merge>

Dans les cas courants, vous avez déjà corrigé votre TETE DÉTACHÉE car elle était liée à l'un des problèmes de configuration ci-dessus.

fixation de la TÊTE DÉTACHÉE lorsque .update = checkout

$ cd <submodule-path> # and make modification to your submodule
$ git add .
$ git commit -m"Your modification" # Let's say you forgot to push it to remote.
$ cd <parent-repo-path>
$ git status # you will get
Your branch is up-to-date with '<origin>/<branch>'.
Changes not staged for commit:
    modified:   path/to/submodule (new commits)
# As normally you would commit new commit hash to your parent repo
$ git add -A
$ git commit -m"Updated submodule"
$ git push <origin> <branch>.
$ git status
Your branch is up-to-date with '<origin>/<branch>'.
nothing to commit, working directory clean
# If you now update your submodule
$ git submodule update --remote
Submodule path 'path/to/submodule': checked out 'commit-hash'
$ git status # will show again that (submodule has new commits)
$ cd <submodule-path>
$ git status
HEAD detached at <hash>
# as you see you are DETACHED and you are lucky if you found out now
# since at this point you just asked git to update your submodule
# from remote master which is 1 commit behind your local branch
# since you did not push you submodule chage commit to remote. 
# Here you can fix it simply by. (in submodules path)
$ git checkout <branch>
$ git push <origin>/<branch>
# which will fix the states for both submodule and parent since 
# you told already parent repo which is the submodules commit hash 
# to track so you don't see it anymore as untracked.

Mais si vous avez déjà réussi à apporter des modifications localement pour le sous-module et que vous avez commité, si vous les avez poussées vers remote puis lorsque vous avez exécuté 'git checkout', Git vous en informe:

$ git checkout <branch>
Warning: you are leaving 1 commit behind, not connected to any of your branches:
If you want to keep it by creating a new branch, this may be a good time to do so with:

L'option recommandée pour créer une branche temporaire peut être bonne, puis vous pouvez simplement fusionner ces branches, etc. Cependant, personnellement, j'utiliserais juste git cherry-pick <hash>dans ce cas.

$ git cherry-pick <hash> # hash which git showed you related to DETACHED HEAD
# if you get 'error: could not apply...' run mergetool and fix conflicts
$ git mergetool
$ git status # since your modifications are staged just remove untracked junk files
$ rm -rf <untracked junk file(s)>
$ git commit # without arguments
# which should open for you commit message from DETACHED HEAD
# just save it or modify the message.
$ git push <origin> <branch>
$ cd <parent-repo-path>
$ git add -A # or just the unstaged submodule
$ git commit -m"Updated <submodule>"
$ git push <origin> <branch>

Bien qu'il existe d'autres cas où vous pouvez mettre vos sous-modules dans l'état DETACHED HEAD, j'espère que vous comprenez maintenant un peu plus comment déboguer votre cas particulier.

mkungla
la source
2
HEAD détaché est le comportement par défaut de git submodule update --remote. Jetez un œil à la réponse de Simba, je pense que cela devrait être la bonne réponse.
magomar
78

L'ajout d'une branchoption dans .gitmodulen'est PAS du tout lié au comportement détaché des sous-modules. L'ancienne réponse de @mkungla est incorrecte ou obsolète.

De git submodule --help, HEAD détaché est le comportement par défaut de git submodule update --remote.

Tout d'abord, il n'est pas nécessaire de spécifier une branche à suivre . origin/masterest la branche par défaut à suivre.

--éloigné

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. La télécommande utilisée est remote ( branch.<name>.remote) de la branche , par défautorigin . La branche distante utilisée par défaut estmaster .

Pourquoi

Alors pourquoi HEAD est-il détaché après update? Ceci est causé par le comportement de mise à jour du module par défaut:checkout .

--check-out

Récupérez le commit enregistré dans le superprojet sur un HEAD détaché dans le sous-module. Il s'agit du comportement par défaut , l'utilisation principale de cette option est de remplacer submodule.$name.updatelorsqu'elle est définie sur une valeur autre que checkout.

Pour expliquer ce comportement de mise à jour étrange, nous devons comprendre comment fonctionnent les sous-modules?

Citation de Démarrer avec des sous-modules dans le livre Pro Git

Bien que sbmodule DbConnectorsoit un sous-répertoire dans votre répertoire de travail, Git le voit comme un sous-module et ne suit pas son contenu lorsque vous n'êtes pas dans ce répertoire. Au lieu de cela, Git le voit comme un commit particulier de ce référentiel .

Le référentiel principal suit le sous-module avec son état à un point spécifique , l' ID de validation . Ainsi, lorsque vous mettez à jour des modules, vous mettez à jour l'ID de validation vers un nouveau.

Comment

Si vous souhaitez que le sous-module soit fusionné automatiquement avec la branche distante, utilisez --mergeou --rebase.

--fusionner

Cette option n'est valide que pour la commande de mise à jour . Fusionnez le commit enregistré dans le superprojet dans la branche actuelle du sous-module. Si cette option est donnée, la HEAD du sous-module ne sera pas détachée .

--refase

Rebase la branche actuelle sur le commit enregistré dans le superprojet. Si cette option est donnée, la HEAD du sous-module ne sera pas détachée .

Tout ce que tu as à faire c'est,

git submodule update --remote --merge
# or
git submodule update --remote --rebase

Alias ​​recommandé:

git config alias.supdate 'submodule update --remote --merge'

# do submodule update with
git supdate

Il existe également une option pour créer --mergeou --rebasecomme comportement par défaut de git submodule update, en définissant submodule.$name.updatesur mergeou rebase.

Voici un exemple de configuration du comportement de mise à jour par défaut de la mise à jour du sous-module dans .gitmodule.

[submodule "bash/plugins/dircolors-solarized"]
    path = bash/plugins/dircolors-solarized
    url = https://github.com/seebi/dircolors-solarized.git
    update = merge # <-- this is what you need to add

Ou configurez-le en ligne de commande,

# replace $name with a real submodule name
git config -f .gitmodules submodule.$name.update merge

Références

Simba
la source
6
J'utilise git submodule update --remote --merge, et il abaisse le sous-module dans un état détaché. Également essayé --rebaseavec le même résultat.
Joe Strout
8
@JoeStrout Si votre sous-module a déjà été détaché, corrigez l'état détaché avant de faire une mise à jour avec les commandes ci-dessus. cddans le sous - module, le sous - module à la caisse une branche spécifique, git checkout master.
Simba
2
Ou - si c'est trop compliqué pour plusieurs sous-modules (récursifs) - faites simplement git submodule foreach --recursive git checkout master.
stefanct
1
Je ne comprends que partiellement les descriptions «comment fonctionne git». TBH Je ne suis pas vraiment intéressé à comprendre comment fonctionne git, je veux juste l'utiliser. Maintenant, je comprends que je peux réparer les sous-modules détachés avec git submodule foreach --recursive git checkout master. Mais comment puis-je empêcher git de toujours les détacher? La définition des options de configuration pour chaque sous - module n'est pas une option!
Nicolas
Pour moi, l'exécution git submodule update --remote --mergene laissait pas le sous-module dans un état HEAD détaché, mais l'exécution git submodule updateaprès l'édition de mon .gitmodulefichier comme vous l'avez indiqué AIT laissé le sous-module dans un état HEAD détaché.
bweber13 le
41

J'en ai eu marre qu'il se détache toujours donc j'utilise juste un script shell pour le construire pour tous mes modules. je suppose que tous les sous-modules sont sur le maître: voici le script:

#!/bin/bash
echo "Good Day Friend, building all submodules while checking out from MASTER branch."

git submodule update 
git submodule foreach git checkout master 
git submodule foreach git pull origin master 

exécutez-le depuis votre module parent

j2emanue
la source
2
git submodule foreach git pull origin master - c'est ce que je cherchais .. bravo
csomakk
simple et concis! Merci!
zhekaus le
12

Découvrez ma réponse ici: Sous-modules Git: Spécifiez une branche / une balise

Si vous le souhaitez, vous pouvez ajouter manuellement la ligne "branch = master" dans votre fichier .gitmodules. Lisez le lien pour voir ce que je veux dire.

EDIT: Pour suivre un projet de sous-module existant dans une succursale, suivez les instructions de VonC ici à la place:

Sous-modules Git: spécifiez une branche / une balise

Johnny Z
la source
14
Les réponses sont supposées être en ligne; Le lien vers les réponses de l'IIRC est un faux pas de Stack Overflow.
Tony Topper
1
@TonyTopper Même lorsque vous vous connectez simplement à une autre réponse SO? Seuls les liens externes de l'IIRC sont désapprouvés car ils peuvent disparaître, puis le lien est mort et la réponse est, eh bien, inutile. Pourtant, il n'y a pas un tel danger avec les réponses SO, elles ne disparaîtront jamais, à moins que SO ne disparaisse (et peuvent être restaurées quoi qu'il arrive). Il a également répondu à la question, comme branch = master" line into your .gitmodulec'est en fait la réponse complète, a résolu ce problème pour moi.
Mecki
9

L'autre façon de faire en sorte que votre sous-module extrait la branche est d'aller dans le .gitmodulesfichier dans le dossier racine et d'ajouter le champ branchdans la configuration du module comme suit:

branch = <branch-name-you-want-module-to-checkout>

frontendgirl
la source
15
Pour moi, cela ne fonctionne pas. J'ai correctement réglé branch = my_wanted_branch. Mais le faire fonctionner git submodule update --remotevérifie toujours que la tête est détachée.
Andrius
Faites ceci, puis cd sudmodule & git co thebranche & cd .., puis git submodule update --remote et ça marche!
pdem
N'est-ce pas ainsi que '.gitmodules' est en cours d'utilisation active (est en cours de lecture) uniquement pendant que le superprojet est cloné de manière récursive sous-module ou que le sous-module est initialisé? En d'autres termes, votre propre référentiel vous mettez à jour le fichier dans lequel ne bénéficie pas toujours des mises à jour de configuration des sous-modules placés dans '.gitmodules'. Dans ma compréhension, «.gitmodules» est un modèle de configuration créé pendant que le dépôt est cloné.
Na13-c
3

Comme d'autres personnes l'ont dit, la raison pour laquelle cela se produit est que le référentiel parent ne contient qu'une référence (au SHA1 de) un commit spécifique dans le sous-module - il ne sait rien sur les branches. Voici comment cela devrait fonctionner: la branche qui était à ce commit peut avoir avancé (ou reculer), et si le référentiel parent avait référencé la branche, il pourrait facilement se rompre lorsque cela se produit.

Cependant, en particulier si vous développez activement à la fois le repo parent et le sous-module, l' detached HEADétat peut être déroutant et potentiellement dangereux. Si vous effectuez des commits dans le sous-module alors qu'il est en detached HEADétat, ceux-ci deviennent suspendus et vous pouvez facilement perdre votre travail. (Les commits en suspens peuvent généralement être sauvés en utilisant git reflog, mais il est préférable de les éviter en premier lieu.)

Si vous êtes comme moi, alors la plupart du temps, s'il y a une branche dans le sous-module qui indique que le commit est extrait, vous préférez vérifier cette branche plutôt que d'être dans l'état HEAD détaché au même commit. Vous pouvez le faire en ajoutant l'alias suivant à votre gitconfigfichier:

[alias]
    submodule-checkout-branch = "!f() { git submodule -q foreach 'branch=$(git branch --no-column --format=\"%(refname:short)\" --points-at `git rev-parse HEAD` | grep -v \"HEAD detached\" | head -1); if [[ ! -z $branch && -z `git symbolic-ref --short -q HEAD` ]]; then git checkout -q \"$branch\"; fi'; }; f"

Maintenant, après cela, il git submodule updatevous suffit d'appeler git submodule-checkout-branch, et tout sous-module qui est extrait à un commit qui a une branche pointant vers lui va extraire cette branche. Si vous n'avez pas souvent plusieurs branches locales pointant toutes vers le même commit, alors cela fera généralement ce que vous voulez; sinon, alors au moins, cela garantira que tous les commits que vous effectuez vont sur une branche réelle au lieu d'être laissés en suspens.

De plus, si vous avez configuré git pour mettre à jour automatiquement les sous-modules lors du paiement (en utilisant git config --global submodule.recurse true, voir cette réponse ), vous pouvez créer un hook post-checkout qui appelle automatiquement cet alias:

$ cat .git/hooks/post-checkout 
#!/bin/sh
git submodule-checkout-branch

Ensuite, vous n'avez pas besoin d'appeler git submodule updateou git submodule-checkout-branch, le simple fait de git checkoutmettre à jour tous les sous-modules avec leurs commits respectifs et de vérifier les branches correspondantes (si elles existent).

deltacrux
la source
0

La solution la plus simple est:

git clone --recursive [email protected]:name/repo.git

Puis cd dans le répertoire repo et:

git submodule update --init
git submodule foreach -q --recursive 'git checkout $(git config -f $toplevel/.gitmodules submodule.$name.branch || echo master)'
git config --global status.submoduleSummary true

Lecture supplémentaire: Bonnes pratiques des sous-modules Git .

noccoa0
la source