Primeiro, vamos esclarecer o que é HEAD e o que significa quando é desanexado.
HEAD é o nome simbólico para a confirmação de saída no momento. Quando o HEAD não é desanexado (a situação "normal" 1 : você tem uma ramificação com check-out), o HEAD na verdade aponta para o "ref" de um ramo e o ramo aponta para o commit. HEAD é, portanto, "anexado" a um ramo. Quando você faz uma nova confirmação, o ramo que aponta para HEAD é atualizado para apontar para a nova confirmação. HEAD segue automaticamente, uma vez que apenas aponta para o ramo.
git symbolic-ref HEAD
rendimentos refs/heads/master
O ramo chamado "mestre" é retirado.
git rev-parse refs/heads/master
yield 17a02998078923f2d62811326d130de991d1a95a
Esse commit é a dica ou "cabeça" atual do ramo mestre.
git rev-parse HEAD
também produz 17a02998078923f2d62811326d130de991d1a95a
Isto é o que significa ser uma "referência simbólica". Aponta para um objeto através de alguma outra referência.
(As referências simbólicas foram originalmente implementadas como links simbólicos, mas posteriormente foram alteradas para arquivos simples com interpretação extra, para que pudessem ser usados em plataformas que não possuem links simbólicos.)
Temos HEAD
→ refs/heads/master
→17a02998078923f2d62811326d130de991d1a95a
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 desanexado como estando em um ramo sem nome.
git symbolic-ref HEAD
falha com fatal: ref HEAD is not a symbolic ref
git rev-parse HEAD
yields 17a02998078923f2d62811326d130de991d1a95a
Como não é uma referência simbólica, deve apontar diretamente para o próprio commit.
Temos HEAD
→17a02998078923f2d62811326d130de991d1a95a
O importante a lembrar com um HEAD desanexado é que, se o commit apontado não for referenciado (nenhum outro árbitro pode alcançá-lo), ele ficará "dangling" quando você fizer o checkout de outro commit. Eventualmente, essas confirmações pendentes serão removidas pelo processo de coleta de lixo (por padrão, elas são mantidas por pelo menos 2 semanas e podem ser mantidas por mais tempo, sendo referenciadas pelo reflog da HEAD).
1
É perfeitamente bom fazer um trabalho “normal” com um HEAD separado, basta acompanhar o que você está fazendo para evitar ter que pescar o histórico abandonado do reflog.
As etapas intermediárias de um rebase interativo são realizadas com um HEAD desanexado (parcialmente para evitar poluir o reflog do ramo ativo). Se você concluir a operação de rebase completa, ele atualizará sua ramificação original com o resultado cumulativo da operação de rebase e reconectará HEAD à ramificação original. Meu palpite é que você nunca concluiu completamente o processo de rebase; isso deixará você com um HEAD desanexado apontando para o commit que foi processado mais recentemente pela operação de rebase.
Para se recuperar da sua situação, você deve criar um ramo que aponte para o commit atualmente apontado pelo seu HEAD desanexado:
git branch temp
git checkout temp
(esses dois comandos podem ser abreviados como git checkout -b temp
)
Isso conectará novamente seu HEAD ao novo temp
ramo.
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
(Provavelmente, você desejará experimentar as opções de log: adicione -p
, deixe --pretty=…
para ver toda a mensagem de log etc.)
Se o seu novo temp
ramo parecer bom, convém atualizar (por exemplo) master
para apontar para ele:
git branch -f master temp
git checkout master
(esses dois comandos podem ser abreviados como git checkout -B master temp
)
Você pode excluir a ramificação temporária:
git branch -d temp
Por fim, você provavelmente desejará enviar o histórico restabelecido:
git push origin master
Você pode precisar adicionar --force
ao final deste comando para enviar por push se a ramificação remota não puder ser "encaminhada rapidamente" para a nova confirmação (ou seja, você eliminou ou reescreveu alguma confirmação existente ou, de outra forma, reescreveu um pouco do histórico).
Se você estava no meio de uma operação de rebase, provavelmente deveria limpá-la. Você pode verificar se uma rebase estava em processo procurando o diretório .git/rebase-merge/
. Você pode limpar manualmente o rebase em andamento apenas excluindo esse diretório (por exemplo, se você não se lembrar mais da finalidade e do contexto da operação de rebase ativa). Geralmente você usaria git rebase --abort
, mas isso faz algumas redefinições extras que você provavelmente deseja evitar (ele move o HEAD de volta para o ramo original e o redefine para o commit original, o que desfará parte do trabalho que fizemos acima).