Como desfazer o “git commit --amend” feito em vez do “git commit”

1295

Eu acidentalmente alterei meu commit anterior. O commit deveria ter sido separado para manter o histórico das alterações que fiz em um arquivo específico.

Existe uma maneira de desfazer o último commit? Se eu fizer algo comogit reset --hard HEAD^ , o primeiro commit também será desfeito.

(Ainda não enviei para nenhum diretório remoto)

Jesper Rønn-Jensen
fonte

Respostas:

2291

O que você precisa fazer é criar uma nova confirmação com os mesmos detalhes que a HEADconfirmação atual , mas com o pai como a versão anterior HEAD. git reset --softmoverá o ponteiro da ramificação para que a próxima confirmação ocorra em cima de uma confirmação diferente de onde está o cabeçalho da ramificação atual.

# Move the current head so that it's pointing at the old commit
# Leave the index intact for redoing the commit.
# HEAD@{1} gives you "the commit that HEAD pointed at before 
# it was moved to where it currently points at". Note that this is
# different from HEAD~1, which gives you "the commit that is the
# parent node of the commit that HEAD is currently pointing to."
git reset --soft HEAD@{1}

# commit the current tree using the commit details of the previous
# HEAD commit. (Note that HEAD@{1} is pointing somewhere different from the
# previous command. It's now pointing at the erroneously amended commit.)
git commit -C HEAD@{1}
CB Bailey
fonte
33
Muito legal, +1. Eu até fiz isso com a segunda última alteração alterar git reflogpara encontrar o número correto, por exemplo {2}.
JJD 12/07
179
Só para esclarecer, o primeiro comando é um verdadeiro "desfazer". Ele produz o HEAD, o diretório de trabalho (inalterado) e o estado do índice antes de git commit --amend. O segundo é um "refazer" em um novo commit. Eles funcionam para qualquer um git commit, não apenas --amend.
precisa saber é o seguinte
60
Portanto, se você não alterou com uma nova mensagem de confirmação que precisa recuperar, a segunda parte pode ser apenas regular git commit.
21412 Montado
18
Por alguma razão, eu estava ficando um erro durante a execução git reset --soft HEAD@{1}: fatal: ambiguous argument 'HEAD@1': unknown revision or path not in the working tree. Use '--' to separate paths from revisions. Quando substituí HEAD@{1}pelo hash de commit equivalente mostrado em git reflog(obrigado JJD!), Essa resposta funcionou maravilhosamente!
precisa
20
@ TimArnold, dependendo do seu shell, pode ser necessário colocar aspas simples ou duplas HEAD@{1}. Se eu rodar echo HEAD@{1}no tcsh, por exemplo, a saída é HEAD@1porque as chaves foram interpretadas pelo tcsh. Se eu usar aspas simples, as chaves são preservadas.
Kelvin
136

use o ref-log :

git branch fixing-things HEAD@{1}
git reset fixing-things

você deve ter todas as alterações alteradas anteriormente apenas na sua cópia de trabalho e pode confirmar novamente

para ver uma lista completa dos índices anteriores git reflog

knittl
fonte
7
Isso limpa o índice também - ainda é útil, mas vai além de um simples "desfazer".
precisa saber é o seguinte
3
existe alguma diferença entre HEAD@{1}e HEAD~1?
Neaumusic
15
@neaumusic: sim! HEAD~1é exatamente o mesmo HEAD^e identifica o pai do commit atual. HEAD@{1}por outro lado, refere-se ao commit que o HEAD apontou antes deste, ou seja, eles significam commits diferentes quando você faz checkout de um ramo diferente ou altera um commit.
knittl
@knittl ah Não admira que eu não acho que isso era possível antes, obrigado novamente, uma boa informação
neaumusic
9
o primeiro passo é redundante. Simples git reset HEAD@{1}é o suficiente.
dwelle
79

Encontre seus commits alterados por:

git log --reflog

Nota: Você pode adicionar --patchpara ver o corpo dos commit para maior clareza. Igual agit reflog .

redefina seu HEAD para qualquer confirmação anterior no momento em que estiver correto:

git reset SHA1 --hard

Nota: Substitua SHA1 pelo seu hash de confirmação real. Observe também que este comando perderá todas as alterações não confirmadas, portanto você pode ocultá-las antes. Como alternativa, use --softpara manter as alterações mais recentes e, em seguida, confirme-as.

Em seguida, escolha a outra confirmação que você precisa sobre ela:

git cherry-pick SHA1
kenorb
fonte
26
Se você fizer isso git reset SHA1 --soft, poderá reter as alterações mais recentes e depois confirmar.
pravj
24

Você sempre pode dividir um commit, a partir do manual

  • Inicie uma rebase interativa com git rebase -i commit ^, em que commit é o commit que você deseja dividir. De fato, qualquer intervalo de confirmação serve, desde que contenha esse commit.
  • Marque o commit que você deseja dividir com a ação "edit".
  • Quando se trata de editar essa confirmação, execute git reset HEAD ^. O efeito é que o HEAD é rebobinado por um e o índice segue o exemplo. No entanto, a árvore de trabalho permanece a mesma.
  • Agora adicione as alterações ao índice que você deseja ter no primeiro commit. Você pode usar o git add (possivelmente interativamente) ou o git-gui (ou ambos) para fazer isso.
  • Confirme o índice agora atual com qualquer mensagem de confirmação apropriada agora.
  • Repita as duas últimas etapas até que sua árvore de trabalho esteja limpa.
  • Continue a rebase com git rebase --continue.
Arkaitz Jimenez
fonte
26
muito complicado. git reflogé tudo que você precisa
knittl
2
Muitas etapas, sim, mas cada etapa é simples e fácil de executar. Isso funcionou para mim e obtém meu voto.
OzBandit
5
Além disso, esta resposta permite que você escolha seletivamente as alterações que você acidentalmente 'alterou', para fornecer algum valor adicional à abordagem git reset --soft HEAD @ {1} (que resolveu meu problema)
Wiebe Tijsma
2
Você também pode selecionar seletivamente alterações com o método reflog. Apenas faça em git resetvez de git reset --soft, e faça git add --patch.
geekofalltrades
1
Isso ainda reescreve a história e requer um empurrão forçado. Dependendo da sua situação, isso pode ou não ser um problema.
Pajn 12/06
20

Possivelmente, vale a pena notar que, se você ainda estiver no seu editor com a mensagem de confirmação, poderá excluir a mensagem de confirmação e ela abortará o git commit --amendcomando.

Justin Schulz
fonte
É esse.
atilkan 10/02
Salve o meu but ^^
engineercoding
14

Talvez possa usar git reflogpara obter dois commit antes de emendar e depois de emendar.

Em seguida, use git diff before_commit_id after_commit_id > d.diffpara obter diferenças entre antes e depois da alteração.

Próximo uso git checkout before_commit_idpara voltar antes de confirmar

E último uso git apply d.diffpara aplicar a mudança real que você fez.

Isso resolve meu problema.

utzcoz
fonte
11

Se você enviou a confirmação para remotamente e, em seguida, alterou erroneamente as alterações nessa confirmação, isso resolverá o seu problema. Emita a git logpara localizar o SHA antes da confirmação. (isso pressupõe que remoto é denominado origem). Agora emita esses comandos usando esse SHA.

git reset --soft <SHA BEFORE THE AMMEND>
#you now see all the changes in the commit and the amend undone

#save ALL the changes to the stash
git stash

git pull origin <your-branch> --ff-only
#if you issue git log you can see that you have the commit you didn't want to amend

git stash pop
#git status reveals only the changes you incorrectly amended

#now you can create your new unamended commit
David Sopko
fonte
3
Este é um caso especial da questão mais geral, mas cobriu exatamente minha necessidade imediata.
dmckee --- gatinho ex-moderador
8

Você pode fazer abaixo para desfazer sua git commit —amend

  1. git reset --soft HEAD^
  2. git checkout files_from_old_commit_on_branch
  3. git pull origin your_branch_name

====================================

Agora suas alterações são como as anteriores. Então você está pronto para desfazergit commit —amend

Agora você pode fazer git push origin <your_branch_name>, empurrar para o ramo.

Pratik
fonte
3

Quase 9 anos atrasado para isso, mas não vi essa variação mencionada realizando a mesma coisa (é uma combinação de algumas delas, semelhante à resposta principal ( https://stackoverflow.com/a/1459264/4642530 ) .

Pesquisar todas as cabeças destacadas na ramificação

git reflog show origin/BRANCH_NAME --date=relative

Em seguida, encontre o hash SHA1

Redefinir para SHA1 antigo

git reset --hard SHA1

Em seguida, empurre-o de volta.

git push origin BRANCH_NAME

Feito.

Isso fará com que você retorne totalmente ao antigo commit.

(Incluindo a data do cabeçalho de confirmação separado e sobrescrito)

garrettmac
fonte
Sim, mas normalmente quero redefinir --softpara manter minhas alterações. Eu só quero que cometeu separadamente
Juan Mendes
2
  1. Fazer checkout para ramificação temporária com o último commit

    git branch temp HEAD@{1}

  2. Redefinir última confirmação

    git reset temp

  3. Agora, você terá todos os arquivos confirmados e confirmados anteriormente. Verifique o status de todos os arquivos.

    git status

  4. Redefina seus arquivos de confirmação do estágio git.

    git reset myfile1.js (em breve)

  5. Anexe novamente este commit

    git commit -C HEAD@{1}

  6. Adicione e confirme seus arquivos para o novo commit.

Priyanshu Chauhan
fonte