Erro de referência do sub-módulo Git 'referência não é uma árvore'

305

Eu tenho um projeto com um submódulo que está apontando para uma confirmação inválida: a submissão do submódulo permaneceu local e quando tento buscá-lo em outro repositório, recebo:

$ git submodule update
fatal: reference is not a tree: 2d7cfbd09fc96c04c4c41148d44ed7778add6b43
Unable to checkout '2d7cfbd09fc96c04c4c41148d44ed7778add6b43' in submodule path 'mysubmodule'

Eu sei o que o CABEÇA submodule deveria ser, existe alguma maneira eu posso mudar isso localmente, sem empurrar a partir do repo que faz ter cometer 2d7cfbd09fc96c04c4c41148d44ed7778add6b43?

Não tenho certeza se estou sendo claro ... aqui está uma situação semelhante que encontrei.

Mauricio Scheffer
fonte
11
"fatal: reference is not a tree" em referência a submódulos geralmente parece significar que o submódulo confirma que o repositório pai espera ainda não ter sido enviado por push ou está estragado de alguma outra maneira. Para nós, essa mensagem de erro confusa foi resolvida pressionando apenas um submódulo que alguém esqueceu de enviar.
Chris Moschini 27/01
1
@ ChrisMoschini - Acabei de ter esse problema, e essa era minha "solução", empurrei e puxei o repositório principal., Mas esqueci de enviar meu último commit ao repositório do submódulo. Obrigado!
Rotem
Talvez você se esqueceu de empurrar as últimas commits submódulo
Hafenkranich

Respostas:

378

Supondo que o repositório do submódulo contenha uma confirmação que você deseja usar (diferente da confirmação que é referenciada no estado atual do superprojeto), há duas maneiras de fazê-lo.

O primeiro requer que você já conheça o commit do submódulo que deseja usar. Funciona de dentro para fora, ajustando diretamente o submódulo e atualizando o superprojeto. O segundo funciona de “fora para dentro”, localizando o commit do superprojeto que modificou o submódulo e, em seguida, redefinindo o índice do superprojeto para se referir a um commit do submódulo diferente.

De dentro para fora

Se você já sabe que cometem você deseja que o sub-módulo para uso, cda sub-módulo, consulte a cometer você quer, então git adde git commit-lo de volta na super-projeto.

Exemplo:

$ git submodule update
fatal: reference is not a tree: e47c0a16d5909d8cb3db47c81896b8b885ae1556
Unable to checkout 'e47c0a16d5909d8cb3db47c81896b8b885ae1556' in submodule path 'sub'

Ops, alguém fez um commit do superprojeto que se refere a um commit não publicado no submódulo sub. De alguma forma, já sabemos que queremos que o submódulo esteja no commit 5d5a3ee314476701a20f2c6ec4a53f88d651df6c. Vá lá e confira diretamente.

Fazer checkout no submódulo

$ cd sub
$ git checkout 5d5a3ee314476701a20f2c6ec4a53f88d651df6c
Note: moving to '5d5a3ee314476701a20f2c6ec4a53f88d651df6c' which isn't a local branch
If you want to create a new branch from this checkout, you may do so
(now or later) by using -b with the checkout command again. Example:
  git checkout -b <new_branch_name>
HEAD is now at 5d5a3ee... quux
$ cd ..

Como estamos verificando um commit, isso produz um HEAD desanexado no submódulo. Se você quiser ter certeza de que o submódulo está usando uma ramificação, use git checkout -b newbranch <commit>para criar e efetuar check-out de uma ramificação no commit ou check-out do branch que você deseja (por exemplo, um com o commit desejado na ponta).

Atualizar o superprojeto

Um check-out no submódulo é refletido no superprojeto como uma alteração na árvore de trabalho. Portanto, precisamos organizar a mudança no índice do superprojeto e verificar os resultados.

$ git add sub

Confira os resultados

$ git submodule update
$ git diff
$ git diff --cached
diff --git c/sub i/sub
index e47c0a1..5d5a3ee 160000
--- c/sub
+++ i/sub
@@ -1 +1 @@
-Subproject commit e47c0a16d5909d8cb3db47c81896b8b885ae1556
+Subproject commit 5d5a3ee314476701a20f2c6ec4a53f88d651df6c

A atualização do submódulo foi silenciosa porque o submódulo já está no commit especificado. O primeiro diff mostra que o índice e a árvore de trabalho são iguais. O terceiro diff mostra que a única alteração faseada é mover o subsubmódulo para uma confirmação diferente.

Confirmar

git commit

Isso confirma a entrada do sub-módulo corrigido.


De fora para dentro

Se você não tiver certeza de qual commit você deve usar no submódulo, consulte o histórico no superprojeto para guiá-lo. Você também pode gerenciar a redefinição diretamente do superprojeto.

$ git submodule update
fatal: reference is not a tree: e47c0a16d5909d8cb3db47c81896b8b885ae1556
Unable to checkout 'e47c0a16d5909d8cb3db47c81896b8b885ae1556' in submodule path 'sub'

Esta é a mesma situação que acima. Mas desta vez vamos nos concentrar em corrigi-lo do superprojeto, em vez de mergulhar no submódulo.

Encontre o compromisso incorreto do superprojeto

$ git log --oneline -p -- sub
ce5d37c local change in sub
diff --git a/sub b/sub
index 5d5a3ee..e47c0a1 160000
--- a/sub
+++ b/sub
@@ -1 +1 @@
-Subproject commit 5d5a3ee314476701a20f2c6ec4a53f88d651df6c
+Subproject commit e47c0a16d5909d8cb3db47c81896b8b885ae1556
bca4663 added sub
diff --git a/sub b/sub
new file mode 160000
index 0000000..5d5a3ee
--- /dev/null
+++ b/sub
@@ -0,0 +1 @@
+Subproject commit 5d5a3ee314476701a20f2c6ec4a53f88d651df6c

OK, parece que deu errado ce5d37c, então vamos restaurar o submódulo a partir do pai ( ce5d37c~).

Como alternativa, você pode pegar o commit do submódulo no texto do patch ( 5d5a3ee314476701a20f2c6ec4a53f88d651df6c) e usar o processo "dentro, fora" acima.

Checkout no Superprojeto

$ git checkout ce5d37c~ -- sub

Isso redefiniu a entrada do sub-módulo para subo que estava no commit ce5d37c~no superprojeto.

Atualize o submódulo

$ git submodule update
Submodule path 'sub': checked out '5d5a3ee314476701a20f2c6ec4a53f88d651df6c'

A atualização do submódulo foi OK (indica um HEAD desanexado).

Confira os resultados

$ git diff ce5d37c~ -- sub
$ git diff
$ git diff --cached
diff --git c/sub i/sub
index e47c0a1..5d5a3ee 160000
--- c/sub
+++ i/sub
@@ -1 +1 @@
-Subproject commit e47c0a16d5909d8cb3db47c81896b8b885ae1556
+Subproject commit 5d5a3ee314476701a20f2c6ec4a53f88d651df6c

O primeiro diff mostra que subagora é o mesmo em ce5d37c~. O segundo diff mostra que o índice e a árvore de trabalho são iguais. O terceiro diff mostra que a única mudança faseada está movendo o subsubmódulo para um commit diferente.

Confirmar

git commit

Isso confirma a entrada do sub-módulo corrigido.

Chris Johnsen
fonte
Na abordagem "Fora, dentro", você poderia esclarecer por que "parece que deu errado no ce5d37c?" Que dedos aquele que os maus cometem?
Garrett Albright
5
@Garrett: A suposição é de que e47c0aum commit não existe no repositório local sub, mas os subpontos do superprojeto apontam para esse commit. Isso pode ter acontecido porque outra pessoa criada e47c0aem sua cópia do sub, atualizou seu superprojeto para apontar para esse commit e enviou o superprojeto sem enviar e47c0apara o repositório central / compartilhado sub. Quando extraímos do superprojeto central / compartilhado, obtemos um commit que aponta subpara e47c0a, mas não podemos "ver" esse commit. ce5d37cé suspeito porque, com base no diff, foi introduzido e47c0a.
Chris Johnsen
Ainda é deixado um pouco vago onde está o hash específico do submantido no repositório pai que o possui como um submódulo e se ele pode ou não ser manipulado diretamente diretamente para o HEAD atual sub, sem depender de um estado antigo do pai. repo, que nem sempre pode ajudar.
matanster
187

tente isto:

git submodule sync
git submodule update
Lonre Wang
fonte
2
Não é para mim, infelizmente, um dos nossos submódulos foi alvejado pelo principal repositório git com um comando add, agora tendo dificuldades para desfazer isso
Daniel
9
Trabalhou para mim também. Gostaria de saber por que embora.
21712 BenBtg
12
Acontece que fazer a git submodule syncé necessário em cenários em que a URL do controle remoto para um determinado sub-módulo foi alterada. No nosso caso, adicionamos nosso submódulo de um repositório público e, em seguida, alteramos o URL para uma bifurcação privada - e nos metemos nesse pickle específico.
Samscam
Por exemplo: eu tinha um repositório (A) configurado com um submódulo apontando para o meu repositório no Github (B). Criei uma ramificação no repositório A porque queria apontar B no repositório do github de outra pessoa. Depois de lutar um pouco com isso e confirmar a ramificação, mudei meu repositório A de volta para o mestre e tive esse problema com o repositório B. A solução de Leonre Wang a corrigiu.
Fbicknel 20/05
2
Supondo que ninguém REALMENTE tenha estragado (nesse caso, você precisaria de uma excelente resposta de Chris Johnsen) a resposta de Lonre Wang deve resolver o problema, ... A menos que seus submódulos tenham submódulos próprios (e o problema está dentro de um submódulo). Nesse caso, você precisa cd no submódulo que possui o submódulo com o problema e executar os comandos acima. Observe que a atualização possui uma opção --recursive (atualização do sub-módulo git --recursive), mas a sincronização não; você realmente precisa executar manualmente 'git submodule sync' dentro do submódulo que possui o sub (sub) módulo problemático. Este foi o meu problema;).
Carlo Wood
16

Este erro pode significar que falta uma confirmação no submódulo. Ou seja, o repositório (A) possui um submódulo (B). A deseja carregar B para que ele esteja apontando para um determinado commit (em B). Se esse commit estiver ausente, você receberá esse erro. Uma vez possível causa: a referência ao commit foi enviada por A, mas o commit real não foi enviado por B. Então, eu começaria por aí.

Menos provável, há um problema de permissões, e a confirmação não pode ser obtida (possível se você estiver usando git + ssh).

Certifique-se de que os caminhos do submodulo estejam bem em .git / config e .gitmodules.

Uma última coisa a tentar - dentro do diretório do submódulo: git reset HEAD --hard

Daniel Tsadok
fonte
3
Eu já expliquei que na pergunta ... a pergunta em si era como resolvê-lo. E já foi respondido com sucesso há quase dois anos ... Permissões não têm nada a ver com isso.
Mauricio Scheffer
1
Você declarou, certamente não explicou.
Daniel Tsadok
O que quero dizer é que essa resposta não adiciona nenhuma informação valiosa, eu a excluí.
Mauricio Scheffer
4
o "git reset HEAD --hard" também me ajudou ... nada mais funcionou. Eu tentei as soluções anteriores também, sem dados. Obrigado!
Virgil
1
Cada segmento é seu próprio mundinho online. O que você diz representa você - você não pode esperar que as pessoas estudem sua história pessoal para tentar enquadrar seus comentários em um contexto que lhe conceda o respeito que você deseja. Seja gentil, respeitoso e não precisará pedir que as pessoas entendam suas peculiaridades pessoais. Se você puder ler seu comentário de um contexto neutro, como faria alguém de fora, entenderá minhas críticas.
precisa saber é o seguinte
10

Possível causa

Isso pode acontecer quando:

  1. Os submódulos foram editados no local
  2. Submódulo (s) confirmado (s), que atualiza o hash do submódulo apontado para
  3. Submódulo (s) não enviado (s) .

por exemplo, algo assim aconteceu:

$ cd submodule
$ emacs my_source_file  # edit some file(s)
$ git commit -am "Making some changes but will forget to push!"

Deveria ter o submódulo enviado neste momento.

$ cd .. # back to parent repository
$ git commit -am "updates to parent repository"
$ git push origin master

Como resultado, as confirmações ausentes não puderam ser encontradas pelo usuário remoto porque elas ainda estão no disco local.

Solução

Informa a pessoa que modificou o submódulo para empurrar, ou seja,

$ cd submodule
$ git push
chriskelly
fonte
6

Eu recebi este erro quando o fiz:

$ git submodule update --init --depth 1

mas a confirmação no projeto pai estava apontando para uma confirmação anterior.

Excluindo a pasta do submódulo e executando:

$ git submodule update --init

NÃO resolveu o problema. Excluí o repositório e tentei novamente sem a sinalização de profundidade e funcionou.

Este erro ocorre no Ubuntu 16.04 git 2.7.4, mas não no Ubuntu 18.04 git 2.17, o TODO encontra a correção ou versão exata da correção.

Platão
fonte
minha equipe, desde então, submódulos abandonado em nosso caminho de código muito trabalho lol
Platão
1
qual foi sua alternativa?
Nellolilo
@nuzzolilo que adicionamos username/repo#shaao package.json, uma opção muito mais flexível é organizar seu sistema com um conjunto de contêineres de encaixe
Platão
3
Isso é tão chato. --depth=1economiza muita largura de banda quando não preciso do histórico de repositórios. Se alguém encontrar ou souber por que isso está acontecendo, eu adoraria saber.
I336_
@ i336_ Embora não possa explicar o porquê, escrevi um auxiliar cmake que ajuda a atenuar o problema aqui: github.com/LMMS/lmms/blob/… . Ele usa uma deinitabordagem que corrige o problema na maioria das vezes. Quando empacotado com um sistema de compilação, o usuário final pode apenas deixar o sistema de compilação buscar os submódulos e abandonar recursivecompletamente o comando quebrado . Ainda existem cenários em que isso ocorre, como o submódulo pressionou a força e eliminou completamente o commit.
tresf
5

Isso também pode acontecer quando você tem um submódulo apontando para um repositório que foi reformulado e a confirmação fornecida "desapareceu". Embora a confirmação ainda esteja no repositório remoto, ela não está em uma ramificação. Se você não pode criar uma nova ramificação (por exemplo, não o seu repositório), você precisará atualizar o super projeto para apontar para uma nova confirmação. Como alternativa, você pode enviar uma de suas cópias dos submódulos para outro local e, em seguida, atualizar o superprojeto para apontar para esse repositório.

pasamio
fonte
5

Sua filial pode não estar atualizada, é uma solução simples, mas tente git fetch

kittycatbytes
fonte
2

Esta resposta é para usuários do SourceTree com experiência limitada no terminal git.

Abra o submódulo problemático de dentro do projeto Git (superprojeto).

Busque e verifique se 'Buscar todas as tags' está marcado.

Rebase puxe seu projeto Git.

Isso resolverá o problema de 'referência não é uma árvore' 9 em cada dez vezes. Essa 1 vez, não será, é uma correção terminal, conforme descrito pela resposta superior.

ericTbear
fonte
1

Seu histórico de submódulo é preservado com segurança no submódulo git de qualquer maneira.

Então, por que não excluir o submódulo e adicioná-lo novamente?

Caso contrário, você tentou editar manualmente o HEADou refs/master/headdentro do submódulo.git

Lakshman Prasad
fonte
1
Isso não vai funcionar, pois em algum lugar há uma referência a 2d7cfbd09fc96c04c4c41148d44ed7778add6b43 que é apenas no repo local, em outro lugar, mas não publicado
Mauricio Scheffer
1

Só para ter certeza, tente atualizar seus gitbinários.

O GitHub para Windows tem a versão git version 1.8.4.msysgit.0que, no meu caso, foi o problema. A atualização resolveu.

Gman
fonte
1

No meu caso, nenhuma das respostas acima resolve o problema, mesmo que sejam boas respostas. Então, eu posto minha solução (no meu caso, existem dois clientes git, clientes A e B):

  1. vá para o diretório do submódulo:

    cd sub
    
  2. checkout para dominar:

    git checkout master
    
  3. rebase para um código de confirmação que ambos os clientes podem ver

  4. volte para o diretório do pai:

  5. comprometer-se a dominar

  6. mude para o outro cliente e faça rebasenovamente.

  7. finalmente funciona bem agora! Talvez perca alguns commits, mas funciona.

  8. Para sua informação, não tente remover seu submódulo, ele permanecerá .git/moduleslá e não poderá ser lido novamente, a menos que seja reativo.

kimimaro
fonte
1

Para sincronizar o repositório git com a cabeça do submódulo, caso isso seja realmente o que você deseja, descobri que remover o submódulo e depois lê-lo evita mexer com a história. Infelizmente, remover um submódulo requer hackers, em vez de ser um único comando git, mas factível.

Etapas que segui para remover o submódulo, inspirado em https://gist.github.com/kyleturner/1563153 :

  1. Execute git rm --cached
  2. Exclua as linhas relevantes do arquivo .gitmodules.
  3. Exclua a seção relevante de .git / config.
  4. Exclua os arquivos do submodulo agora não rastreados.
  5. Remova o diretório .git / modules /

Novamente, isso pode ser útil se tudo o que você deseja é apontar para a cabeça do submódulo novamente, e você não complicou as coisas ao manter intacta a cópia local do submódulo. Ele pressupõe que você tenha o submódulo "certo" como seu próprio repositório, seja qual for a origem, e você só quer voltar a incluí-lo adequadamente como submódulo.

Nota: sempre faça uma cópia completa do seu projeto antes de se envolver com esses tipos de manipulação ou qualquer comando git além do simples commit ou push. Eu recomendaria isso com todas as outras respostas também, e como uma diretriz geral do git.

matanster
fonte
1

Só me deparei com esse problema, e nenhuma dessas soluções funcionou para mim. O que acabou por ser a solução para o meu problema é realmente muito mais simples: atualize o Git. O meu era 1.7.1 e, depois de atualizá-lo para 2.16.1 (mais recente), o problema desapareceu sem deixar rasto! Acho que estou deixando aqui, espero que ajude alguém.

An Phan
fonte