Como posso reconciliar o HEAD desanexado com o mestre / origem?

Eu sou novo nas complexidades de ramificação do Git. Eu sempre trabalho em um único ramo e confirmo alterações e, em seguida, periodicamente empurro para minha origem remota.

Em algum lugar recentemente, eu fiz um reset de alguns arquivos para tirá-los do staging de commit, e depois fiz um rebase -i para me livrar de alguns commits locais recentes. Agora estou em um estado que não entendo muito bem.

Na minha área de trabalho, o git log mostra exatamente o que eu esperava – estou no trem certo com os commits que eu não queria que fossem, e os novos lá, etc.

Mas eu apenas empurrei para o repository remoto, e o que está lá é diferente – alguns dos commits que eu matei no rebase foram empurrados, e os novos commits localmente não estão lá.

Eu acho que “mestre / origem” é desanexado do HEAD, mas não estou 100% claro sobre o que isso significa, como visualizá-lo com as ferramentas de linha de comando e como corrigi-lo.

Primeiro, vamos esclarecer o que é HEAD e o que significa quando ele é separado.

HEAD é o nome simbólico para a confirmação atualmente retirada. Quando o HEAD não é desanexado (a situação “normal” 1 : você tem um branch com check out), o HEAD na verdade aponta para o “ref” de um branch e o branch aponta para o commit. CABEÇA é assim “anexada” a um ramo. Quando você faz um novo commit, o branch para o qual o HEAD aponta é atualizado para apontar para o novo commit. HEAD segue automaticamente, uma vez que apenas aponta para o ramo.

  • git symbolic-ref HEAD produz refs/heads/master
    O ramo denominado “mestre” está com check-out.
  • git rev-parse refs/heads/master rendimento git rev-parse refs/heads/master 17a02998078923f2d62811326d130de991d1a95a
    Esse commit é a dica atual ou “head” do branch master.
  • git rev-parse HEAD também produz 17a02998078923f2d62811326d130de991d1a95a
    Isto é o que significa ser um “ref simbólico”. Aponta para um object através de alguma outra referência.
    (Refs simbólicos foram originalmente implementados como links simbólicos, mas depois alterados para arquivos simples com interpretação extra para que pudessem ser usados ​​em plataformas que não possuem links simbólicos).

Nós temos HEADrefs/heads/master17a02998078923f2d62811326d130de991d1a95a

Quando o HEAD é desanexado, ele aponta diretamente para um commit – em vez de apontar indiretamente para um através de um branch. Você pode pensar em um HEAD separado como estando em uma ramificação sem nome.

  • git symbolic-ref HEAD falha com fatal: ref HEAD is not a symbolic ref
  • git rev-parse HEAD rende 17a02998078923f2d62811326d130de991d1a95a
    Como não é uma referência simbólica, deve apontar diretamente para o compromisso em si.

Temos HEAD17a02998078923f2d62811326d130de991d1a95a

O importante a ser lembrado com um HEAD separado é que, se o commit para o qual ele aponta não for referenciado (nenhum outro ref poderá alcançá-lo), ele ficará “pendurado” quando você fizer o checkout de algum outro commit. Eventualmente, tais commits pendentes serão removidos através do processo de garbage collection (por padrão, eles são mantidos por pelo menos 2 semanas e podem ser mantidos por mais tempo sendo referenciados pelo reflog do HEAD).

1 É perfeitamente bom fazer um trabalho “normal” com um HEAD separado, você apenas tem que manter o controle do que está fazendo para evitar ter que apagar o histórico do reflog.


As etapas intermediárias de um rebase interativo são feitas com um HEAD separado (parcialmente para evitar poluir o reflogo do ramo ativo). Se você concluir a operação completa de rebase, ela atualizará sua ramificação original com o resultado cumulativo da operação de rebase e reconectará HEAD na ramificação original. Meu palpite é que você nunca completou completamente o processo de rebase; Isso deixará você com um HEAD separado apontando para o commit que foi processado mais recentemente pela operação de rebase.

Para recuperar-se da sua situação, você deve criar uma ramificação que aponte para a confirmação atualmente apontada por sua CABEÇA desanexada:

 git branch temp git checkout temp 

(esses dois comandos podem ser abreviados como git checkout -b temp )

Isso irá reconectar seu HEAD ao novo ramo temp .

Em seguida, você deve comparar o commit atual (e seu histórico) com o branch normal no qual você esperava estar trabalhando:

 git log --graph --decorate --pretty=oneline --abbrev-commit master origin/master temp git diff master temp git diff origin/master temp 

(Você provavelmente vai querer experimentar as opções de log: add -p , leave off --pretty=… para ver a mensagem de log completa, etc.)

Se o seu novo ramo temp parece bom, você pode querer atualizar (por exemplo) master para apontar para ele:

 git branch -f master temp git checkout master 

(estes dois comandos podem ser abreviados como git checkout -B master temp )

Você pode então excluir o ramo temporário:

 git branch -d temp 

Finalmente, você provavelmente desejará empurrar o histórico restabelecido:

 git push origin master 

Você pode precisar adicionar --force ao final deste comando para empurrar se a ramificação remota não puder ser “encaminhada rapidamente” para o novo commit (ou seja, você descartou, reescreveu algum commit existente ou reescreveu algum histórico ).

Se você estivesse no meio de uma operação de rebase, provavelmente deveria limpá-lo. Você pode verificar se um rebase estava em processo procurando pelo diretório .git/rebase-merge/ . Você pode limpar manualmente o rebase em andamento apenas excluindo esse diretório (por exemplo, se não mais se lembrar da finalidade e do contexto da operação de rebase ativa). Normalmente você usaria git rebase --abort , mas isso faz algum reset extra que você provavelmente quer evitar (ele move HEAD de volta para o branch original e o redefine de volta para o commit original, que irá desfazer parte do trabalho que fizemos acima ).

Apenas faça isso:

 git checkout master 

Ou, se você tiver alterações que deseja manter, faça o seguinte:

 git checkout -b temp git checkout -B master temp 

Eu me deparei com essa questão e quando li na resposta mais votada:

HEAD é o nome simbólico para a confirmação atualmente retirada.

Eu pensei: Ah-ha! Se HEAD é o nome simbólico para a confirmação de check-in atual, posso reconciliá-lo com o master , rebazendo-o contra master :

 git rebase HEAD master 

Este comando:

  1. check out master
  2. identifica as confirmações pai de HEAD volta ao ponto que o HEAD divergiu do master
  3. joga aqueles comete no topo do master

O resultado final é que todas as confirmações que estavam no HEAD mas não no master , também estão no master . master permanece com check-out.


Em relação ao controle remoto:

alguns dos commits que eu matei no rebase foram enviados, e os novos cometidos localmente não estão lá.

O histórico remoto não pode mais ser encaminhado rapidamente usando seu histórico local. Você precisará forçar o push ( git push -f ) para sobrescrever o histórico remoto. Se você tem algum colaborador, geralmente faz sentido coordenar isso com eles para que todos estejam na mesma página.

Depois que você envia master para origin remota, sua origin/master ramificação de rastreamento remoto será atualizada para apontar para a mesma confirmação que o master .

Veja aqui a explicação básica da cabeça destacada:

http://git-scm.com/docs/git-checkout

Linha de comando para visualizá-lo:

 git branch 

ou

 git branch -a 

você terá saída como abaixo:

 * (no branch) master branch1 

O * (no branch) mostra que você está na cabeça imparcial.

Você poderia ter chegado a este estado, fazendo um git checkout somecommit etc. e teria avisado com o seguinte:

Você está no estado ‘HEAD’ desanexado. Você pode olhar em volta, fazer alterações experimentais e cometê-las, e você pode descartar quaisquer commits que você fizer nesse estado sem afetar qualquer ramificação executando outro checkout.

Se você quiser criar uma nova ramificação para reter os commits criados, poderá fazê-lo (agora ou mais tarde) usando -b novamente com o comando checkout. Exemplo:

git checkout -b new_branch_name

Agora, para colocá-los no master:

Faça um git reflog ou até mesmo git log e anote seus commits. Agora git checkout master e git merge os commits.

 git merge HEAD@{1} 

Editar:

Para adicionar, use git rebase -i não apenas para deletar / matar commits que você não precisa, mas também para editá-los. Apenas mencione “edit” na lista de git rebase --continue e você poderá alterar o seu commit e então emitir um git rebase --continue para prosseguir. Isso teria assegurado que você nunca veio para uma CABEÇA separada.

Obtenha seu commit desanexado em seu próprio branch

Simplesmente execute git checkout -b mynewbranch .

Em seguida, execute git log e você verá que commit agora é HEAD nesta nova ramificação.

Se você tem apenas ramificação mestre e quer voltar para “desenvolver” ou um recurso apenas faça isso:

 git checkout origin/develop 

Nota: verificar a origem / desenvolvimento .

Você está no estado HEAD separado . Você pode olhar em volta, fazer alterações experimentais e cometê-las, e você pode descartar quaisquer commits que você fizer nesse estado sem afetar qualquer ramificação executando outro checkout …

então

 git checkout -b develop 

Funciona 🙂

Se você quer empurrar seu atual HEAD separado (verifique git log antes), tente:

 git push origin HEAD:master 

para enviar seu HEAD desanexado para o branch master na origem. Se o seu envio for rejeitado, tente git pull origin master primeiro para obter as alterações de origem. Se você não se importa com as mudanças de origem e é rejeitado, porque você fez algum rebase intencional e deseja replace origem / mestre com sua ramificação atualmente destacada – então você pode forçá-lo ( -f ). Caso você tenha perdido algum access a commits anteriores, você sempre pode executar o git reflog para ver o histórico de todos os branches.


Para voltar a um branch master, enquanto mantém as alterações, tente os seguintes comandos:

 git rebase HEAD master git checkout master 

Veja: Git: “Não está atualmente em nenhum branch.” Existe uma maneira fácil de voltar a um ramo, mantendo as alterações?

O seguinte funcionou para mim (usando apenas o mestre de filial):

 git push origin HEAD:master git checkout master git pull 

O primeiro envia o HEAD separado para a origem remota.

O segundo move-se para o mestre de ramificação.

O terceiro recupera a CABEÇA que fica anexada ao mestre de ramificação.

Problemas podem surgir no primeiro comando se o push for rejeitado. Mas isso não seria mais um problema de cabeça solta, mas é sobre o fato de que o HEAD separado não está ciente de algumas mudanças remotas.

Se você está completamente certo de que HEAD é o bom estado:

 git branch -f master HEAD git checkout master 

Você provavelmente não pode empurrar para origem, desde que seu mestre tenha divergido da origem. Se tiver certeza de que ninguém mais está usando o repository, você pode forçar o envio:

 git push -f 

Mais útil se você estiver em um ramo de resources que ninguém mais está usando.

Tudo o que você precisa fazer é ‘git checkout [nome-da-ramificação]’ onde [nome-da-ramificação] é o nome da ramificação original da qual você entrou em um estado de cabeça desanexada. O (separado do asdfasdf) desaparecerá.

Então, por exemplo, na ramificação ‘dev’ você registra o commit asdfasd14314 ->

 'git checkout asdfasd14314' 

você está agora em um estado de cabeça destacada

‘git branch’ irá listar algo como ->

 * (detached from asdfasdf) dev prod stage 

mas para sair do estado de cabeça desanexado e voltar para dev ->

 'git checkout dev' 

e então ‘git branch’ irá listar ->

 * dev prod stage 

mas isso é claro, se você não pretende manter quaisquer alterações do estado de cabeça desanexado, mas eu me vejo fazendo muito isso, não pretendo fazer quaisquer alterações, mas apenas olhar para um commit anterior.

Como apontado por Chris, eu tive a seguinte situação

git symbolic-ref HEAD falha com fatal: ref HEAD is not a symbolic ref

No entanto git rev-parse refs/heads/master estava apontando para um bom commit de onde eu poderia recuperar (No meu caso último commit e você pode ver que commit usando git show [SHA]

Eu fiz muitas coisas bagunçadas depois disso, mas o que parece ter sido corrigido é

git symbolic-ref HEAD refs/heads/master

E a cabeça está ligada!

Eu acabei de me deparar com essa questão hoje e tenho certeza que resolvi isso fazendo:

 git branch temp git checkout master git merge temp 

Eu estava no meu computador de trabalho quando descobri como fazer isso e agora estou com o mesmo problema no meu computador pessoal. Então terá que esperar até segunda-feira quando eu voltar ao computador do trabalho para ver exatamente como eu fiz isso.

Eu tive o mesmo problema e resolvi-o seguindo os passos seguintes.

Se você precisa manter suas alterações

  1. Primeiro você precisa executar o comando git checkout master para colocá-lo de volta no branch master.
  2. Se você precisar manter suas alterações, execute o git checkout -b changes e git checkout -B master changes

Se você não precisa de suas alterações

  1. Para remover todos os arquivos não rastreados de sua ramificação, execute git clean -df .

  2. Então você precisa limpar todas as alterações não sincronizadas dentro do seu repository. Para fazer isso, você precisa executar o git checkout --

  3. Finalmente você tem que colocar sua ramificação de volta no branch master usando o comando git checkout master .

Eu tive esse problema hoje, onde eu tinha atualizado um submódulo, mas não estava em nenhum ramo. Eu já tinha cometido, então stashing, checkout, unstashing não funcionaria. Acabei escolhendo o commit da cabeça solta. Então, imediatamente depois que eu cometi (quando o push falhou), eu fiz:

 git checkout master git cherry-pick 99fe23ab 

Meu pensamento foi: estou em uma cabeça imparcial, mas eu quero estar no mestre. Assumindo que meu estado desanexado não seja muito diferente do mestre, se eu pudesse aplicar meu commit ao master, estaria definido. Isso é exatamente o que a cereja faz.

Em vez de fazer a git checkout origin/master

apenas faça o git checkout master

então git branch confirmará sua filial.

Se você fez alguns commits no topo do master e apenas quer “fundir” o master (ex: você quer que o master aponte para o HEAD ), o one-liner seria:

 git checkout -B master HEAD 
  1. Isso cria uma nova ramificação chamada master , mesmo que já exista (que é como mover o master e é isso que queremos).
  2. A ramificação recém-criada é configurada para apontar para HEAD , que é onde você está.
  3. A nova ramificação está com check-out, então você está no master depois.

Achei isso especialmente útil no caso de sub-repositorys, que também estão em um estado separado com bastante frequência.

Para mim, foi tão fácil quanto deletar o branch local novamente, já que eu não tinha nenhum commit local que eu quisesse empurrar:

Então eu fiz:

 git branch -d branchname 

E, em seguida, verificando o ramo novamente:

 git checkout branchname 

Em palavras simples, o estado HEAD desanexado significa que você não está com check-out para HEAD (ou dica) de qualquer ramificação .

Entenda com um exemplo

Um ramo na maioria dos casos é a sequência de múltiplos commits como:

Commit 1: master -> branch_HEAD (123be6a76168aca712aea16076e971c23835f8ca)

Commit 2: master -> 123be6a76168aca712aea16076e971c23835f8ca -> branch_HEAD (100644a76168aca712aea16076e971c23835f8ca)

Como você pode ver acima em caso de sequência de commits, sua ramificação aponta para seu commit mais recente. Então, nesse caso, se você fizer check-out para cometer 123be6a76168aca712aea16076e971c23835f8ca, então você estaria em estado de cabeça desanexado desde HEAD de sua filial aponta para 100644a76168aca712aea16076e971c23835f8ca e tecnicamente você está com check-out no CABEÇA de nenhuma ramificação. Portanto, você está no estado HEAD separado.

Explicação Teórica

Neste Blog está claramente afirmando que um repository Git é uma tree de commits, com cada commit apontando para seu ancestral com cada ponteiro de commits sendo atualizado e esses pointers para cada branch são armazenados nos subdiretórios .git / refs. Tags são armazenadas em .git / refs / tags e branches são armazenados em .git / refs / heads. Se você olhar para qualquer um dos arquivos, você verá que cada tag corresponde a um único arquivo, com um hash commit de 40 caracteres e como explicado acima pelo @Chris Johnsen e @Yaroslav Nikitenko, você pode conferir essas referências.

Eu entrei em um estado realmente bobo, eu duvido que alguém vai achar isso útil …. mas apenas no caso

 git ls-remote origin 0d2ab882d0dd5a6db93d7ed77a5a0d7b258a5e1b HEAD 6f96ad0f97ee832ee16007d865aac9af847c1ef6 refs/heads/HEAD 0d2ab882d0dd5a6db93d7ed77a5a0d7b258a5e1b refs/heads/master 

que eu finalmente consertado com

 git push origin :HEAD 

Isso funcionou para mim perfeitamente:

1. git stash para salvar suas modificações locais

Se você quiser descartar as alterações
git clean -df
git checkout -- .
O git clean remove todos os arquivos não rastreados (aviso: embora não apague arquivos ignorados mencionados diretamente no .gitignore, ele pode excluir arquivos ignorados que residem em pastas) e o git checkout limpa todas as mudanças não sincronizadas.

2. git checkout master para mudar para o ramo principal (supondo que você quer usar master)
3. git pull para puxar último commit do branch master
4. git status , a fim de verificar tudo parece ótimo

 On branch master Your branch is up-to-date with 'origin/master'. 

Quando eu pessoalmente me encontro em uma situação quando acontece que eu fiz algumas mudanças enquanto não estou no master (isto é, o HEAD é separado logo acima do master e não há commits entre eles) stashing pode ajudar:

 git stash # HEAD has same content as master, but we are still not in master git checkout master # switch to master, okay because no changes and master git stash apply # apply changes we had between HEAD and master in the first place 

No meu caso, eu corri o git status , e vi que eu tinha alguns arquivos não rastreados no meu diretório de trabalho.

Para fazer o rebase funcionar, eu tinha que limpá-los (já que eu não precisava deles).

Se você estiver usando o EGit no Eclipse: suponha que seu mestre seja o seu principal ramo de desenvolvimento

  • commita você muda para um branch, normalmente um novo
  • então puxe do controle remoto
  • em seguida, clique com o botão direito no nó do projeto, escolha a equipe e escolha mostrar histórico
  • então clique com o botão direito no master, escolha check out
  • Se o Eclipse lhe disser, existem dois mestres um remoto local, escolha o remoto

Depois disso, você poderá reconectar-se ao mestre de origem.

 git checkout checksum # You could use this to peek previous checkpoints git status # You will see HEAD detached at checksum git checkout master # This moves HEAD to master branch