Como posso mover o HEAD de volta para um local anterior? (Cabeça desanexada) e Desfazer confirma

179

No Git, eu estava tentando fazer uma squash commitfusão em outra ramificação e, em seguida, redefinindo HEADpara o local anterior via:

git reset origin/master

Mas preciso sair disso. Como posso mover HEAD de volta para o local anterior?

Eu tenho o fragmento SHA-1 ( 23b6772) do commit para o qual preciso movê-lo. Como posso voltar a esse commit?

timpone
fonte
12
HEAD é apenas um ponteiro para a sua localização atual (ou revisão para ser mais preciso). git checkout 23b6772deveria fazer.
Yaroslav Administrador
Possível duplicata do repositório Revert Git para um commit anterior #
Andrew C
1
@YaroslavAdmin Não, não deveria . Verificar um commit diretamente é o motivo pelo qual o estado HEAD desanexado aconteceu (já que as ramificações de rastreamento remoto não podem ser verificadas por si mesmas e adiam automaticamente para a confirmação que apontam quando você tenta fazer isso como o OP fez) comment :-) Eu meio que espero que o problema inicial já esteja resolvido ...
RomainValeri 24/03/19

Respostas:

397

Antes de responder, vamos adicionar alguns antecedentes, explicando o que HEADé isso .

First of all what is HEAD?

HEADé simplesmente uma referência ao commit atual (mais recente) no branch atual.
Só pode haver um single HEADa qualquer momento (excluindo git worktree).

O conteúdo de HEADé armazenado dentro .git/HEADe contém os 40 bytes SHA-1 da confirmação atual.


detached HEAD

Se você não está no último commit - o que significa que HEADestá apontando para um commit anterior no histórico, é chamado detached HEAD.

Digite a descrição da imagem aqui

Na linha de comando, será parecido com este - SHA-1 em vez do nome do ramo, pois ele HEADnão está apontando para a ponta do ramo atual:

Digite a descrição da imagem aqui

Digite a descrição da imagem aqui


Algumas opções sobre como recuperar de um HEAD desanexado:


git checkout

git checkout <commit_id>
git checkout -b <new branch> <commit_id>
git checkout HEAD~X // x is the number of commits t go back

Isso fará o checkout do novo ramo apontando para o commit desejado.
Este comando fará o checkout para um determinado commit.
Nesse ponto, você pode criar uma ramificação e começar a trabalhar a partir deste ponto.

# Checkout a given commit.
# Doing so will result in a `detached HEAD` which mean that the `HEAD`
# is not pointing to the latest so you will need to checkout branch
# in order to be able to update the code.
git checkout <commit-id>

# Create a new branch forked to the given commit
git checkout -b <branch name>

git reflog

Você sempre pode usar o reflogtambém.
git reflog exibirá qualquer alteração que tenha atualizado HEADe o check-out da entrada de reflog desejada HEADretornará a esta confirmação.

Toda vez que o HEAD for modificado, haverá uma nova entrada no reflog

git reflog
git checkout HEAD@{...}

Isso o levará de volta ao seu commit desejado

Digite a descrição da imagem aqui


git reset --hard <commit_id>

"Mova" seu HEAD de volta para o commit desejado.

# This will destroy any local modifications.
# Don't do it if you have uncommitted work you want to keep.
git reset --hard 0d1d7fc32

# Alternatively, if there's work to keep:
git stash
git reset --hard 0d1d7fc32
git stash pop
# This saves the modifications, then reapplies that patch after resetting.
# You could get merge conflicts if you've modified things which were
# changed since the commit you reset to.
  • Nota: ( Desde o Git 2.7 ), você também pode usá-lo git rebase --no-autostash.

git revert <sha-1>

"Desfazer" o intervalo de confirmação ou confirmação fornecido.
O comando reset irá "desfazer" quaisquer alterações feitas no commit fornecido.
Um novo commit com o patch de desfazer será confirmado enquanto o commit original também permanecerá no histórico.

# Add a new commit with the undo of the original one.
# The <sha-1> can be any commit(s) or commit range
git revert <sha-1>

Este esquema ilustra qual comando faz o quê.
Como você pode ver lá, reset && checkoutmodifique o HEAD.

Digite a descrição da imagem aqui

CodeWizard
fonte
Se você não estiver no commit mais recente - o que significa que HEAD está apontando para um commit anterior no histórico é chamado de HEAD desanexado, a menos que o commit anterior no histórico seja a ponta de um ramo diferente. Na minha experiência, você poderia dizer que está desanexado se o HEAD não estiver apontando para um commit que também é apontado por qualquer ramificação. Isso não se aplica a tags.
Tim
Você pode estar no HEAD desanexado e, ao mesmo tempo, ter um ramo com o mesmo commit que o HEAD deste ramo. Eu não entendo o seu comentário
CodeWizard
3
Tenho problemas com o uso de marcação inline-código para títulos :)
jub0bs
Não foi possível encontrar uma maneira melhor de enfatizá-lo. fique à vontade para editar. você é mais que bem
CodeWizard
22

Faz

git reset 23b6772

Para ver se você está na posição correta:

git status

Você verá algo

No mestre da filial Sua filial está atrás de 'origem / mestre' em 17 confirmações e pode ser encaminhada rapidamente.

Em seguida, reescreva o histórico no seu controle remoto para refletir a alteração:

git push --force-with-lease // a useful command @oktober mentions in comments
peculiar
fonte
1
Seja extremamente cauteloso com git push --force. Em muitas situações, você será a pessoa menos popular da equipe por um tempo ...
Kay V
para adicionar à nota acima, encontrei esta citação em about.gitlab.com/blog/2014/11/26/keeping-your-code-protected e precisei adicioná-la: "Um único comando git push --force pode arruinar o dia com facilidade para muitas pessoas: os repositórios [186 Jenkins] têm suas cabeças de ramificação rebobinadas para apontar para confirmações mais antigas e, na verdade, as confirmações mais recentes foram perdidas após o mau git-push ".
Kay V
2
@KayV dar uma olhada git push --force-with-lease(artigo Thoughtbot: thoughtbot.com/blog/git-push-force-with-lease )
outubro
1
Bandeira útil, @oktober, e um bom artigo. Obrigado por adicioná-lo aqui e me enviar um ping.
Kay V
1
obrigado! isso me ajudou a desfazer uma má fusão. como as mesclagens não reagem da mesma maneira que revertas confirmações, eu me encontrei em uma situação incrivelmente difícil. force-with-leaseme deu a confiança para reescrever a história git do ramo sem afetar o trabalho de outras pessoas. bravo!
anon58192932
11

Solução mais rápida possível (apenas 1 passo)

Usar git checkout -

Você verá Switched to branch <branch_name>. Confirme se é o ramo que você deseja.


Breve explicação: este comando moverá HEAD de volta à sua última posição. Veja a nota sobre os resultados no final desta resposta.


Mnemônico: essa abordagem é muito parecida com o uso cd -para retornar ao seu diretório visitado anteriormente. A sintaxe e os casos aplicáveis ​​são uma combinação muito boa (por exemplo, é útil quando você realmente deseja que o HEAD retorne para onde estava).


Solução mais metódica (2 etapas, mas memorável)

A abordagem rápida resolve a questão do OP. Mas e se a sua situação for um pouco diferente: digamos que você reiniciou o Bash e se viu com o HEAD desconectado. Nesse caso, aqui estão duas etapas simples e fáceis de lembrar.

1. Escolha o ramo que você precisa

Usar git branch -v

Você vê uma lista de ramificações locais existentes. Pegue o nome da filial que atenda às suas necessidades.

2. Mova HEAD para ele

Usar git checkout <branch_name>

Você verá Switched to branch <branch_name>. Sucesso!


Resultados

Com qualquer um dos métodos, agora você pode continuar adicionando e comprometendo seu trabalho como antes: suas próximas alterações serão rastreadas <branch_name>.

Observe que ambos git checkout -e git checkout <branch_name>fornecerão instruções adicionais se você tiver confirmado alterações enquanto o HEAD foi desanexado.

Kay V
fonte
Isso não está funcionando porque se eu fizer (supondo que 8acc968 seja HEAD ~ 2) git checkout 8acc968, git branch -vtenha MyBranchna lista abaixo ... mas git checkout MyBranchexclua meus comentários.
amuliar
Oi @ peculiar - git checkout 8acc968irá verificar um commit, não um branch. Se MyBranchtiver as confirmações que você deseja, tente git checkout MyBranch. Se não contiver as alterações no commit 8acc968, você precisará mesclar essas alterações depois de verificar a ramificação.
Kay V
Obrigado pela resposta! Eu fiz git checkoutpara ver um commit anterior e queria voltar para o commit mais recente. Mas sem o último hash de confirmação, eu estava praticamente perdido. Esta solução é perfeita para a minha situação!
zyy 10/02
4

A pergunta pode ser lida como:

Eu estava no estado desanexado com HEADat 23b6772e digitado git reset origin/master(porque queria esmagar). Agora mudei de idéia, como volto a HEADestar 23b6772?

A resposta direta é: git reset 23b6772

Mas eu respondi a essa pergunta porque estava cansado de digitar (copiar e colar) hashes de commit ou sua abreviação toda vez que queria fazer referência ao anterior HEADe estava pesquisando no Google para ver se havia algum tipo de abreviação.

Acontece que existe!

git reset -(ou no meu caso git cherry-pick -)

Que, aliás, era o mesmo que cd -retornar ao diretório atual anterior no * nix! Então, viva, eu aprendi duas coisas com uma pedra.

Antak
fonte
0

Quando você executa o comando git checkout commit_id, o HEAD desanexado 13ca5593d(say commit-id)e o branch ficam mais disponíveis.

Voltar para o local anterior, execute o comando passo a passo -

  1. git pull origin branch_name (diga mestre)
  2. git checkout branch_name
  3. git pull origin branch_name

Você voltará ao local anterior com uma confirmação atualizada do repositório remoto.

Deepak Kumar
fonte
0

Hoje, fiz check-out por engano em um commit e comecei a trabalhar nele, fazendo alguns commit em um estado HEAD desanexado. Em seguida, enviei para a ramificação remota usando o seguinte comando:

git push origin HEAD: <My-remote-branch>

Então

git checkout <My-remote-branch>

Então

git pull

Finalmente recebi todas as minhas alterações em meu ramo que fiz para desanexar HEAD.

Zaid Mirza
fonte
0

Isso pode não ser uma solução técnica, mas funciona. (se alguém do seu companheiro de equipe tiver a mesma filial no local)

Vamos assumir o nome do seu ramo como branch-xxx .

Passos para resolver:

  • Não atualize ou puxe - nada
  • Basta criar uma nova ramificação ( ramificação-aaaa ) a partir de ramificação-xxx em sua máquina
  • Isso é tudo, todas as suas alterações existentes estarão neste novo ramo ( ramo-aaaa ). Você pode continuar seu trabalho com este ramo.

Nota: Novamente, essa não é uma solução técnica, mas ajudará com certeza.

Sathish Kumar
fonte