Por que meu Git Submodule HEAD foi desconectado do master?


163

Eu estou usando submódulos Git. Depois de extrair alterações do servidor, muitas vezes meu cabeçalho do submodulo é desanexado da ramificação principal.

Por que isso acontece?

Eu tenho que fazer sempre:

git branch
git checkout master

Como posso garantir que meu submódulo esteja sempre apontando para o ramo principal?



@bitoiu Olhei para a subárvore e o Google Repo. Ainda não tenho a solução perfeita :(
om471987 12/12

1
minha experiência com módulos gits, em um ambiente de IC é péssima, talvez outras pessoas tenham experiências melhores.
bitoiu 12/09

@JohnnyZ Thanks. Entendi que o submódulo aponta para um commit e não para o chefe da árvore. Mas por que se separou do ramo. Se você tem um ramo não deveria ser ligado a ele por padrão
om471987

3
Não seja rápido demais para descartar os submódulos apenas porque você soube que eles são ruins. Elas são uma solução ruim se você deseja uma integração contínua, mas são uma solução quase perfeita se você deseja incorporar código de um projeto externo e gerenciar explicitamente todos os pulls. Geralmente, essa é a melhor prática se você estiver se integrando a um módulo não bifurcado que não seja controlado por sua organização. O problema é que eles são uma solução tentadora em todos os tipos de outras situações em que não funcionam muito bem. O melhor conselho é ler como eles funcionam e avaliar seu cenário.
Sarah G

Respostas:


176

EDITAR:

Consulte @Simba Answer para obter uma solução válida

submodule.<name>.updateé o que você deseja alterar, consulte os documentos - padrãocheckout
submodule.<name>.branch especificar ramo remoto a ser rastreado - padrãomaster


RESPOSTA ANTIGA:

Pessoalmente, eu odeio respostas aqui que direcionam para links externos que podem parar de funcionar com o tempo e verifique minha resposta aqui (a menos que a pergunta seja duplicada) - direcionando para a pergunta que cobre o assunto entre as linhas de outro assunto, mas no geral é igual a: "Estou não responder, leia a documentação ".

Então, voltando à pergunta: por que isso acontece?

Situação que você descreveu

Depois de extrair alterações do servidor, muitas vezes meu cabeçalho do submodulo é desanexado da ramificação principal.

Este é um caso comum quando não se usa submódulos com muita frequência ou apenas se inicia com submódulos . Acredito que estou correto ao afirmar que todos já estivemos lá em algum momento em que o HEAD do nosso submódulo é desanexado.

  • Causa: seu submódulo não está rastreando a ramificação correta (mestre padrão).
    Solução: verifique se o submódulo está rastreando a ramificação correta
$ 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>
  • Causa: seu repositório pai não está configurado para rastrear ramificações de submódulos.
    Solução: Faça seu submódulo rastrear sua ramificação remota adicionando novos submódulos com os dois comandos a seguir.
    • Primeiro você diz ao git para rastrear seu controle remoto <branch>.
    • você diz ao git para executar uma nova alteração ou mesclagem, em vez de fazer o checkout
    • você diz ao git para atualizar seu submódulo do controle remoto.
    $ git submodule add -b <branch> <repository> [<submodule-path>]
    $ git config -f .gitmodules submodule.<submodule-path>.update rebase
    $ git submodule update --remote
  • Se você não adicionou seu submódulo existente como esse, você pode facilmente corrigir isso:
    • Primeiro, você quer ter certeza de que seu submódulo possui a ramificação que você deseja rastrear.
    $ 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>

Nos casos mais comuns, você já corrigiu agora o seu CABEÇOTE DETALHADO, pois estava relacionado a um dos problemas de configuração acima.

fixação da CABEÇA DESTACADA quando .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.

Mas se você conseguiu fazer algumas alterações localmente já para o submódulo e confirmado, envie-as para remoto, quando você executar o 'git checkout', o Git notificará você:

$ 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:

A opção recomendada para criar uma ramificação temporária pode ser boa e, em seguida, você pode mesclar essas ramificações etc. No entanto, eu pessoalmente usaria apenas git cherry-pick <hash>neste caso.

$ 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>

Embora existam mais casos, você pode colocar seus submódulos no estado DETACHED HEAD, espero que você entenda agora um pouco mais sobre como depurar seu caso específico.


2
HEAD desanexado é o comportamento padrão de git submodule update --remote. Por favor, dê uma olhada na resposta de Simba, acho que deve ser a resposta certa.
magomar 6/09/19

78

Adicionando uma branchopção em .gitmodulese não relacionadas ao comportamento individual de submódulos em tudo. A resposta antiga de @mkungla está incorreta ou obsoleta.

De git submodule --help, HEAD desanexado é o comportamento padrão de git submodule update --remote.

Primeiro, não há necessidade de especificar um ramo a ser rastreado . origin/masteré o ramo padrão a ser rastreado.

--controlo remoto

Em vez de usar o SHA-1 registrado do superprojeto para atualizar o submódulo, use o status da ramificação de rastreamento remoto do submódulo. O controle remoto usado é o controle remoto da filial ( branch.<name>.remote), por padrãoorigin . A filial remota usou como padrãomaster .

Por quê

Então, por que o HEAD é destacado depois update? Isto é causado pelo comportamento de atualização do módulo padrão:checkout .

--Verificação de saída

Faça o checkout do commit registrado no superprojeto em um HEAD desanexado no submódulo. Esse é o comportamento padrão , o principal uso dessa opção é substituir submodule.$name.updatequando definido como um valor diferente de checkout.

Para explicar esse estranho comportamento de atualização, precisamos entender como funcionam os submódulos?

Citação de Começando com submódulos no livro Pro Git

Embora sbmodule DbConnectorseja um subdiretório no seu diretório de trabalho, o Git o vê como um submódulo e não rastreia seu conteúdo quando você não está nesse diretório. Em vez disso, o Git o vê como um commit específico desse repositório .

O repositório principal rastreia o submódulo com seu estado em um ponto específico , o ID de confirmação . Portanto, quando você atualiza os módulos, está atualizando o ID de confirmação para um novo.

Quão

Se você deseja que o submódulo seja mesclado automaticamente com a ramificação remota, use --mergeou --rebase.

--merge

Esta opção é válida apenas para o comando update . Mesclar a confirmação registrada no superprojeto na ramificação atual do submódulo. Se essa opção for fornecida, o HEAD do submódulo não será desconectado .

--rebase

Rebase a ramificação atual no commit registrado no superprojeto. Se essa opção for fornecida, o HEAD do submódulo não será desconectado .

Tudo que você precisa fazer é,

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

Alias ​​recomendado:

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

# do submodule update with
git supdate

Há também uma opção para criar --mergeou --rebasecomo o comportamento padrão git submodule update, configurando submodule.$name.updatepara mergeou rebase.

Aqui está um exemplo sobre como configurar o comportamento de atualização padrão da atualização do submódulo em .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 configure-o na linha de comando,

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

Referências


6
Eu uso git submodule update --remote --merge, e ele puxa o submódulo em um estado desanexado. Também tentei --rebasecom o mesmo resultado.
Joe Strout 03/09/19

8
@JoeStrout Se o seu submódulo já foi desanexado, corrija o estado desanexado antes de fazer uma atualização com os comandos acima. cdno submódulo, faça o checkout do submódulo para um ramo específico com git checkout master,.
Simba

2
Ou - se isso é muito trabalhoso para vários submódulos (recursivos) - basta fazê-lo git submodule foreach --recursive git checkout master.
stefanct

1
Entendo apenas parcialmente as descrições "como o git funciona". TBH Não estou realmente interessado em entender como o git funciona, só quero usá-lo. Agora entendo que posso consertar sub-módulos desanexados com git submodule foreach --recursive git checkout master. Mas como posso impedir que o git sempre os desanexe? Definir opções de configuração para cada submódulo não é uma opção!
Nicolas

Para mim, a execução git submodule update --remote --mergenão deixou o submódulo em um estado HEAD desanexado, mas a execução git submodule updateapós editar meu .gitmodulearquivo, como você indicou, deixou o submódulo em um estado HEAD desanexado.
bweber13

41

Eu cansei disso sempre desconectando, então eu apenas uso um script de shell para construí-lo para todos os meus módulos. Eu assumo que todos os submódulos estão no master: aqui está o 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 

execute-o a partir do seu módulo pai


2
git submódulo foreach git mestre origem tração - isto é o que eu estava procurando .. elogios
csomakk

simples e conciso! Obrigado!
zhekaus 27/02

12

Confira minha resposta aqui: Sub-módulos Git: especifique uma ramificação / tag

Se desejar, você pode adicionar a linha "branch = master" ao seu arquivo .gitmodules manualmente. Leia o link para ver o que quero dizer.

EDIT: Para rastrear um projeto de submódulo existente em uma filial, siga as instruções do VonC aqui:

Sub-módulos Git: especifique uma ramificação / tag


14
As respostas devem estar em linha; A ligação do IIRC às respostas é uma falsa pilha do Stack Overflow.
Tony Topper

1
@TonyTopper Mesmo quando apenas liga para outra resposta SO? Somente os links externos do IIRC são desaprovados, pois eles podem desaparecer e, em seguida, o link está inoperante e a resposta é, bem, inútil. No entanto, não existe esse perigo com as respostas do SO, elas nunca desaparecem, a menos que o SO desapareça (e possa ser restaurado, aconteça o que acontecer). Ele também respondeu à pergunta, como branch = master" line into your .gitmoduleé de fato a resposta completa, resolveu esse problema para mim.
Mecki

9

A outra maneira de fazer com que seu submódulo verifique a ramificação é acessar o .gitmodulesarquivo na pasta raiz e adicionar o campo branchna configuração do módulo da seguinte maneira:

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


15
Para mim, isso não funciona. Eu configurei corretamente branch = my_wanted_branch. Mas executá- git submodule update --remotelo ainda faz check-out como cabeça destacada.
Andrius 22/03

Faça isso, então cd sudmodule & git co thebranche & cd .., depois git submodule update --remote e ele funciona!
Pdem 04/04

Não é assim que '.gitmodules' está em uso ativo (está sendo lido) apenas enquanto o superprojeto está sendo clonado de maneira recursiva por submódulos ou submodulo sendo inicializado? Em outras palavras, em seu próprio repositório, você atualiza o arquivo no qual nem sempre se beneficia das atualizações de configuração do submódulo colocadas em '.gitmodules'. No meu entendimento, '.gitmodules' é um modelo para a configuração criada enquanto o repo está sendo clonado ou algo assim.
Na13-c 25/07/19

3

Como outras pessoas disseram, a razão pela qual isso acontece é que o repositório pai contém apenas uma referência a (o SHA1 de) um commit específico no submódulo - ele não sabe nada sobre ramificações. É assim que deve funcionar: o ramo que estava naquele commit pode ter avançado (ou retrocedido) e, se o repositório pai tiver feito referência ao branch, ele poderá ser interrompido facilmente quando isso acontecer.

No entanto, especialmente se você estiver desenvolvendo ativamente no repositório pai e no submódulo, o detached HEADestado pode ser confuso e potencialmente perigoso. Se você fizer confirmações no submódulo enquanto estiver no detached HEADestado, elas ficarão pendentes e você poderá facilmente perder seu trabalho. (Os envios dangling geralmente podem ser resgatados usando git reflog, mas é muito melhor evitá-los em primeiro lugar.)

Se você é como eu, na maioria das vezes, se há uma ramificação no submódulo que aponta para o check-out do commit, você prefere fazer check-out desse branch do que estar no estado HEAD desanexado no mesmo commit. Você pode fazer isso adicionando o seguinte alias ao seu gitconfigarquivo:

[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"

Agora, depois de fazer, git submodule updatevocê só precisa chamar git submodule-checkout-branch, e qualquer submódulo com check-out em um commit que tem um ramo apontando para ele fará check-out desse ramo. Se você não costuma ter várias ramificações locais, todas apontando para o mesmo commit, isso geralmente fará o que você deseja; caso contrário, pelo menos, garantirá que quaisquer confirmações efetuadas sejam enviadas para uma ramificação real, em vez de ficar penduradas.

Além disso, se você configurou o git para atualizar automaticamente os submódulos no checkout (usando git config --global submodule.recurse true, veja esta resposta ), você pode criar um gancho pós-checkout que chama esse alias automaticamente:

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

Então você não precisa chamar um git submodule updateou git submodule-checkout-branch, apenas git checkouto update atualiza todos os submódulos para seus respectivos commits e verifica as ramificações correspondentes (se existirem).


0

A solução mais simples é:

git clone --recursive git@github.com:name/repo.git

Em seguida, cd no diretório repo e:

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

Leitura adicional: práticas recomendadas dos submódulos do Git .

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.