web-dev-qa-db-fra.com

Pourquoi mon sous-module GIT HEAD est-il détaché de master?

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

Pourquoi ça se passe?

Je dois toujours faire:

git branch
git checkout master

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

122
om471987

Personnellement, je déteste les réponses qui dirigent vers des liens externes qui risquent de ne plus fonctionner avec le temps et vérifient ma réponse here (sauf si la question est en double) - diriger vers la question qui couvre le sujet entre les lignes d'un autre sujet, mais globalement égale: "Je ne réponds pas, lisez la documentation."

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

La situation que vous avez décrite

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

Ceci est un cas fréquent lorsque l'on n'utilise pas sous-modules trop souvent ou vient de commencer avec sous-modules . Je crois avoir raison de dire que nous avons tous été à un moment donné où notre sous-module ' s HEAD se détache.

  • Cause: votre sous-module ne suit pas de branche ou une branche correcte. 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érant parent n'est pas configuré pour suivre une branche de 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.
    • D'abord, vous dites à git de suivre votre télécommande <branch>.
    • Deuxièmement, vous dites à git de mettre à jour votre sous-module à distance.
    $ git submodule add -b <branch> <repository> [<submodule-path>]
    $ git submodule update --remote
  • Si vous n'avez pas ajouté votre sous-module existant comme ceci, vous pouvez facilement résoudre ce problème:
    • Tout d'abord, vous voulez vous assurer que la branche dont vous souhaitez effectuer le suivi est vérifiée par la branche.
    $ 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>

Cependant, même si vous avez configuré votre sous-module pour suivre la branche correcte, vous pouvez toujours vous trouver dans des situations où votre sous-module obtient HEAD detached at <commit-hash>

Dans les cas courants, vous avez déjà résolu votre DETACHED HEAD puisqu'il était lié à l'un des problèmes de configuration ci-dessus. Mais rappelez-vous que votre référentiel parent ne gère plus l'état de votre sous-module (à l'aide d'un hachage de validation validé dans le référentiel parent), car votre sous-module suit sa propre branche distante, ce qui ouvre de nouvelles possibilités d'échec.

Exécutez $ git status dans votre parent et également dans le chemin du sous-module pour vérifier que tout est suivi correctement et que tout est à jour, puis exécutez $ cd <parent-repo> et git submodule update --remote. Comme vous le voyez, si vous exécutez à nouveau le statut git, tout va bien pour le moment.

Pour démontrer que, lorsque tout semble configuré correctement et que vous ne vous attendez pas à être DÉTACHÉ HEAD les choses peuvent mal se passer, examinons ce qui suit:

$ 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 parvenez déjà à apporter des modifications localement pour le sous-module et que vous les avez validées, les poussez à distance puis, lorsque vous exécutez 'git checkout', Git vous avertit:

$ 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, je n'utiliserais que 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 que dans certains cas, vos sous-modules puissent passer à l'état DETACHED HEAD, j'espère que vous comprendrez maintenant un peu plus comment déboguer votre cas particulier.

168
mkungla

j'en ai marre de toujours le détacher, alors je viens d'utiliser un script Shell pour le construire pour tous mes modules. Je suppose que tous les sous-modules sont sur master: 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 

l'exécuter depuis votre module parent

27
j2emanue

Ajouter une option branch dans .gitmodule est NON lié au comportement détaché des sous-modules. Ce que @mkungla vous a dit est un non-sens.

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

Tout d'abord, il y a pas besoin de spécifier une branche à suivre. Origin/master est la branche par défaut à suivre.

--éloigné

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

Pourquoi

Alors pourquoi HEAD est-il détaché après update? Parce que le comportement par défaut de submodule.$name.update est checkout.

--check-out

Vérifiez la validation enregistrée dans le superprojet sur un HEAD détaché dans le sous-module. C'est le comportement par défaut, cette option est principalement utilisée pour remplacer submodule.$name.update lorsqu'il est défini sur une valeur autre que checkout.

Cela signifie que vous extrayez un ID de validation spécifique par défaut. De toute évidence, cela entraînera une tête isolée.

Comment

Si vous souhaitez que le sous-module soit fusionné automatiquement avec une branche distante, utilisez --merge ou --rebase.

--fusionner

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

--rebase

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

Tout ce que vous devez faire c'est

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

Il existe également une option permettant de définir --merge ou --rebase comme comportement par défaut de git submodule update, en définissant submodule.$name.update sur merge ou rebase.

Voici un exemple de configuration du comportement de mise à jour par défaut de la mise à jour de 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

Toute ma réponse est basée sur le manuel. git submodule --help.

15
Simba

Découvrez ma réponse ici: Sous-modules Git: spécifiez une branche/un tag

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 branche, suivez les instructions de VonC ici:

Sous-modules Git: spécifiez une branche/un tag

10
Johnny Z

L'autre façon de rendre votre sous-module pour vérifier la branche consiste à aller dans le fichier .gitmodules dans le dossier racine et à ajouter le champ branch dans la configuration du module comme suit:

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

8
frontendgirl

Comme d'autres personnes l'ont dit, cela s'explique par le fait que le référent parent ne contient qu'une référence à (le SHA1 de) un commit spécifique dans le sous-module - il ne sait rien des branches. Cela devrait fonctionner ainsi: la branche qui était à cette validation peut avoir avancé (ou reculé), et si le référent parent avait référencé la branche, elle pourrait facilement casser lorsque cela se produirait.

Toutefois, en particulier si vous développez activement à la fois dans le référentiel parent et dans le sous-module, l'état detached HEAD peut être déroutant et potentiellement dangereux. Si vous effectuez des commits dans le sous-module alors qu'il est dans l'état detached HEAD, ils deviennent pendants et vous pouvez facilement perdre votre travail. (Les commits suspendus peuvent généralement être sauvés en utilisant git reflog, mais il est bien 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 pointe vers la validation en cours d'extraction, vous préféreriez vérifier cette branche plutôt que d'être détachée HEAD state à la même validation. Vous pouvez le faire en ajoutant l'alias suivant à votre fichier gitconfig:

[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 avoir fait git submodule update, il vous suffit d'appeler git submodule-checkout-branch, et tout sous-module extrait lors d'une validation avec une branche pointant vers elle vérifiera cette branche. Si vous n'avez pas souvent plusieurs branches locales pointant toutes vers le même commit, cela fera généralement ce que vous voulez; sinon, au moins, cela garantira que tous les commits que vous faites fassent 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 à la caisse (en utilisant git config --global submodule.recurse true, voir cette réponse ), vous pouvez créer un point d'ancrage post-paiement qui appelle automatiquement cet alias:

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

Ensuite, vous n'avez pas besoin d'appeler ni git submodule update ni git submodule-checkout-branch, il vous suffit de faire git checkout pour mettre à jour tous les sous-modules avec leurs commits respectifs et extraire les branches correspondantes (si elles existent).

0
deltacrux