Como desfazer uma escolha bem-sucedida do git?

97

Em um repositório local, acabei de executar git cherry-pick SHA sem conflitos ou problemas. Então percebi que não queria fazer o que acabei de fazer. Eu não empurrei isso para lugar nenhum.

Como posso remover apenas esta escolha certa?

Eu gostaria de saber se existe uma maneira de fazer isso:

  • quando eu tenho outras mudanças locais
  • quando eu não tenho outras mudanças locais

De preferência com um comando para ambos os casos, se possível.

Brad Parks
fonte

Respostas:

133

A escolha certa é basicamente um commit, então se você quiser desfazê-lo, basta desfazer o commit.

quando eu tenho outras mudanças locais

Oculte suas alterações atuais para que você possa aplicá-las novamente após redefinir o commit.

$ git stash
$ git reset --hard HEAD^
$ git stash pop  # or `git stash apply`, if you want to keep the changeset in the stash

quando eu não tenho outras mudanças locais

$ git reset --hard HEAD^
Tim
fonte
6
Além da pergunta e da resposta, se você não tiver certeza do que está escolhendo você pode sempre 'escolher SHA - no-commit' e não haverá commit, apenas alterações que podem ser facilmente verificadas , isso é útil para colheita parcial
kuskmen
1
windows: git reset --hard "HEAD ^"
slim
21

Para desfazer seu último commit, simplesmente faça git reset --hard HEAD~.

Editar : esta resposta se aplica a uma versão anterior da pergunta que não menciona a preservação das mudanças locais; a resposta aceita de Tim é de fato a correta. Agradecimentos a qwertzguy pelo aviso.

David Deutsch
fonte
2
Deve ser HEAD ^ ou HEAD ~ 1
Andreas Wederbrand
Ambos são equivalentes a HEAD ~ (e HEAD ^ 1, nesse caso)
David Deutsch
1
@DavidDeutsch Isso é verdade (embora apenas em versões mais recentes do Git), mas maiúsculas ( HEAD) é mais robusto: considere o caso infeliz em que headé o nome de uma referência existente.
jub0bs de
1
@Jubobs - bom ponto; Eu mudei a caixa da minha resposta.
David Deutsch
1
@qwertzguy, boa pegada; olhando para os carimbos de data / hora, um pouco sobre as mudanças locais foi adicionado à pergunta um minuto depois de eu postar esta resposta :)
David Deutsch
8

Se possível, evite reinicializações forçadas. Reinicializações rígidas são uma das poucas operações destrutivas no git. Felizmente, você pode desfazer uma seleção seletiva sem reinicializações e evitar qualquer coisa destrutiva.

Observe o hash da escolha que deseja desfazer, digamos que seja ${bad_cherrypick}. Faça um git revert ${bad_cherrypick}. Agora, o conteúdo de sua árvore de trabalho é o mesmo antes de sua escolha ruim.

Repita o seu git cherry-pick ${wanted_commit}e quando estiver satisfeito com a nova escolha, faça um git rebase -i ${bad_cherrypick}~1. Durante o rebase, exclua ambos ${bad_cherrypick}e sua reversão correspondente.

O branch em que você está trabalhando terá apenas uma boa escolha. Sem necessidade de redefinições!

Cuadue
fonte
6

git reflog pode vir em seu socorro.

Digite-o em seu console e você obterá uma lista de seu histórico de git junto com SHA-1 que os representa.

Basta verificar qualquer SHA-1 para o qual deseja reverter


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

First of all what is HEAD?

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

O conteúdo de HEADé armazenado dentro .git/HEADe contém os 40 bytes SHA-1 do commit atual.


detached HEAD

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

insira a descrição da imagem aqui

Na linha de comando, será semelhante a este - SHA-1 em vez do nome do branch, pois o HEADnão está apontando para a ponta do branch atual

insira a descrição da imagem aqui

insira a descrição da imagem aqui

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


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 de um novo branch apontando para o commit desejado.
Este comando fará checkout para um determinado commit.
Neste 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 reflogirá exibir qualquer mudança que atualizou o HEADe verificar a entrada de reflog desejada irá definir o HEADretorno para este commit.

Cada vez que o HEAD é modificado, haverá uma nova entrada no reflog

git reflog
git checkout HEAD@{...}

Isso o levará de volta ao commit desejado

insira 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 usar o git rebase --no-autostash.

git revert <sha-1>

"Desfazer" o commit ou intervalo de commit fornecidos.
O comando reset irá "desfazer" quaisquer mudanças feitas no commit fornecido.
Um novo commit com o patch undo será confirmado enquanto o commit original também permanecerá no histórico.

# add 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.

insira a descrição da imagem aqui

CodeWizard
fonte
talvez adicionegit reset --hard
wyx
3

Diante do mesmo problema, descobri que se você se comprometeu e / ou fez push para remoto desde sua escolha bem-sucedida e deseja removê-lo, você pode encontrar o SHA da escolha certa executando:

git log --graph --decorate --oneline

Então, (depois de usar :wqpara sair do log), você pode remover a escolha certa usando

git rebase -p --onto YOUR_SHA_HERE^ YOUR_SHA_HERE

onde YOUR_SHA_HEREé igual ao SHA de 40 ou 7 caracteres abreviado do commit escolhido a dedo.

No início, você não poderá enviar suas alterações porque seu repo remoto e seu repo local terão históricos de commits diferentes. Você pode forçar seus commits locais a substituir o que está em seu controle remoto usando

git push --force origin YOUR_REPO_NAME

(Eu adaptei esta solução de Seth Robertson : Consulte "Removendo um commit inteiro.")

brogrammer
fonte
1

Um comando e não usa o git resetcomando destrutivo :

GIT_SEQUENCE_EDITOR="sed -i 's/pick/d/'" git rebase -i HEAD~ --autostash

Ele simplesmente descarta o commit, colocando você de volta exatamente no estado anterior à escolha seletiva, mesmo que você tenha alterações locais.

qwertzguy
fonte
Ele simplesmente descarta o commit. o que você acha que git reset HEAD^faz?
Tim de
@TimCastelijns git resetdesfaz a ação de confirmação sem eliminar nenhuma das alterações. Recomendo que você leia git-scm.com/docs/git-reset . A questão do OP é desfazer uma escolha seletiva. Tente os dois comandos, o meu o colocará de volta no estado exato em que estava antes da escolha certa. git resetirá bagunçar sua árvore de trabalho à medida que deixa as mudanças selecionadas e elas se tornam indistinguíveis de suas mudanças locais pré-existentes.
qwertzguy
sim, desculpe, eu quis dizer reset --hard. Não deixa alterações
Tim
1
@TimCastelijns git reset --hardé um comando destrutivo que também removerá mudanças locais não relacionadas e fará isso de uma forma irrecuperável! Portanto, não foi isso que o OP pediu.
qwertzguy
2
Essa resposta seria muito mais útil se você explicasse o que esse sedcomando está fazendo, como isso difere de fazer manualmente e o que --autostashfaz.
Chris Page