upstream - Como reverter vários commits git?




publish git tag (8)

Eu tenho um repositório git que se parece com isso:

A -> B -> C -> D -> HEAD

Eu quero que o chefe do ramo aponte para A, ou seja, quero que B, C, D e HEAD desapareçam e quero que a cabeça seja sinônimo de A.

Parece que eu posso tentar rebase (não se aplica, desde que eu empurrei mudanças no meio), ou reverter. Mas como reverter vários commits? Eu reverter um de cada vez? O pedido é importante?


A maneira fácil de reverter um grupo de commits no repositório compartilhado (que as pessoas usam e você quer preservar o histórico) é usar o git revert em conjunto com o git rev-list . Este último irá fornecer-lhe uma lista de commits, o primeiro fará a própria reversão.

Existem duas maneiras de fazer isso. Se você quiser reverter vários commits em um único commit use:

for i in `git rev-list <first-commit-sha>^..<last-commit-sha>`; do git revert -n $i; done

Isso reverterá um grupo de commits que você precisa, mas deixará todas as mudanças em sua árvore de trabalho, você deve cometer todas elas como de costume.

Outra opção é ter um único commit por mudança revertida:

for i in `git rev-list <first-commit-sha>^..<last-commit-sha>`; do git revert --no-edit -s $i; done

Por exemplo, se você tem uma árvore de commits como

 o---o---o---o---o---o--->    
fff eee ddd ccc bbb aaa

para reverter as mudanças de eee para bbb , execute

for i in `git rev-list eee^..bbb`; do git revert --no-edit -s $i; done

Estou tão frustrada que esta questão não pode ser respondida. Todas as outras questões estão relacionadas a como reverter corretamente e preservar a história. Esta pergunta diz "Eu quero que o chefe da filial aponte para A, ou seja, eu quero que B, C, D e HEAD desapareçam e quero que a cabeça seja sinônimo de A."

git checkout <branch_name>
git reset --hard <commit Hash for A>
git push -f

Eu aprendi muito lendo o post de Jakub, mas um cara na empresa (com acesso para empurrar para nossa ramificação "testing" sem Pull-Request) fez 5 commits ruins tentando consertar e consertar um erro que ele cometeu 5 commits atrás. Não apenas isso, mas uma ou duas Solicitações de Pull foram aceitas, que agora eram ruins. Então esqueça, achei o último bom commit (abc1234) e apenas executei o script básico:

git checkout testing
git reset --hard abc1234
git push -f

Eu disse aos outros 5 caras que trabalham neste repo que eles devem tomar nota de suas mudanças nas últimas horas e Wipe / Re-Branch dos últimos testes. Fim da história.


Na minha opinião, uma maneira muito fácil e limpa poderia ser:

voltar para A

git checkout -f A

apontar a cabeça do mestre para o estado atual

git symbolic-ref HEAD refs/heads/master

Salve 

git commit

Nenhum deles funcionou para mim, então eu tinha três commits para reverter (os últimos três commits), então eu fiz:

git revert HEAD
git revert HEAD~2
git revert HEAD~4
git rebase -i HEAD~3 # pick, squash, squash

Funcionou como um encanto :)


Primeiro, certifique-se de que sua cópia de trabalho não seja modificada. Então:

git diff HEAD commit_sha_you_want_to_revert_to | git apply

e depois apenas se comprometer. Não esqueça de documentar qual é o motivo da reversão.


Se você quiser reverter temporariamente as confirmações de um recurso, poderá usar a série de comandos a seguir.

Aqui é como funciona

git log --pretty = oneline | grep 'feature_name' | corte -d '' -f1 | xargs -n1 git revert --no-edit


Esta é uma expansão de uma das soluções fornecidas na resposta de Jakub

Fui confrontado com uma situação em que os commits que eu precisava reverter eram um pouco complexos, com vários dos commits sendo mesclados com commits, e eu precisava evitar reescrever o histórico. Eu não era capaz de usar uma série de comandos git revert porque acabei encontrando conflitos entre as mudanças de reversão sendo adicionadas. Acabei usando os seguintes passos.

Primeiro, confira o conteúdo do commit alvo enquanto deixa o HEAD na ponta do branch:

$ git checkout -f <target-commit> -- .

(O - garante que <target-commit> seja interpretado como um commit em vez de um arquivo; o arquivo. Refere-se ao diretório atual.)

Em seguida, determine quais arquivos foram adicionados nos commits que estão sendo revertidos e, portanto, precisam ser excluídos:

$ git diff --name-status --cached <target-commit>

Os arquivos que foram adicionados devem aparecer com um "A" no início da linha e não deve haver outras diferenças. Agora, se algum arquivo precisar ser removido, monte esses arquivos para remoção:

$ git rm <filespec>[ <filespec> ...]

Por fim, comprometa a reversão:

$ git commit -m 'revert to <target-commit>'

Se desejar, certifique-se de que estamos de volta ao estado desejado:

$git diff <target-commit> <current-commit>

Não deve haver diferenças.


Expandindo o que escrevi em um comentário

A regra geral é que você não deve reescrever (mudar) a história que você publicou, porque alguém pode ter baseado seu trabalho nela. Se você reescrever (alterar) o histórico, você teria problemas ao mesclar as alterações e atualizá-las.

Portanto, a solução é criar um novo commit que reverta as alterações das quais você deseja se livrar. Você pode fazer isso usando o comando git revert .

Você tem a seguinte situação:

A <-- B  <-- C <-- D                                               <-- master <-- HEAD

(setas aqui se referem à direção do ponteiro: a referência "pai" no caso de commits, a top commit no caso de head de ramificação (branch ref) e o nome de branch no caso de referência HEAD).

O que você precisa criar é o seguinte:

A <-- B  <-- C <-- D <-- [(BCD)^-1]                   <-- master <-- HEAD

onde "[(BCD) ^ - 1]" significa o commit que reverte as mudanças em commits B, C, D. A matemática nos diz que (BCD) ^ - 1 = D ^ -1 C ^ -1 B ^ -1, então você pode obter a situação necessária usando os seguintes comandos:

$ git revert --no-commit D
$ git revert --no-commit C
$ git revert --no-commit B
$ git commit -m "the commit message"

A solução alternativa seria checkout conteúdo do commit A e confirmar este estado:

$ git checkout -f A -- .
$ git commit -a

Então você teria a seguinte situação:

A <-- B  <-- C <-- D <-- A'                       <-- master <-- HEAD

O commit A 'tem o mesmo conteúdo que o commit A, mas é um commit diferente (commit message, parents, commit date).

A solução de Jeff Ferland, modificada por Charles Bailey, baseia-se na mesma ideia, mas usa o git reset :

$ git reset --hard A
$ git reset --soft @{1}  # (or ORIG_HEAD), which is D
$ git commit






git-revert