Como reverter vários commits git?

Eu tenho um repository 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 as mudanças no meio), ou reverter. Mas como reverter vários commits? Eu reverti um de cada vez? O pedido é importante?

    Expandindo o que escrevi em um comentário

    A regra geral é que você não deve rewrite (mudar) o histórico que você publicou, porque alguém pode ter baseado seu trabalho nele. Se você rewrite (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 <- mestre <- 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] <- mestre <- CABEÇA
    

    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 verificar o 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 '<- mestre <- 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 

    Para fazer isso você só precisa usar o comando revert , especificando o intervalo de commits que você deseja reverter.

    Levando em conta o seu exemplo, você teria que fazer isso (assumindo que você está no branch ‘master’):

     git revert master~3..master 

    Isto irá criar um novo commit no seu local com o commit inverso de B, C e D (o que significa que ele irá desfazer as mudanças introduzidas por estes commits):

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

    Maneira limpa que eu achei útil

     git revert --no-commit HEAD~3.. 

    Este comando reverte os últimos 3 commits com apenas um commit.

    Também não reescreve a história.

    Semelhante à resposta de Jakub, isso permite que você selecione com facilidade confirmações consecutivas para reverter.

     # revert all commits from B to HEAD, inclusively $ git revert --no-commit B..HEAD $ git commit -m 'message' 
     git reset --hard a git reset --mixed d git commit 

    Isso funcionará como uma reversão para todos eles de uma só vez. Dê uma boa mensagem de commit.

    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.

    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 o histórico. 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  git reset --hard  git push -f 

    Eu aprendi muito lendo o post de Jakub, mas um cara na empresa (com access 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 trabalhando neste repository que eles deveriam tomar nota de suas mudanças nas últimas horas e Wipe / Re-Branch dos últimos testes. Fim da história.

    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 tanto complexos, com vários dos commits sendo mesclados com commits, e eu precisava evitar rewrite 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  -- . 

    (O – garante que 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  

    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 [  ...] 

    Por fim, comprometa a reversão:

     $ git commit -m 'revert to ' 

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

     $git diff   

    Não deve haver diferenças.

    A maneira fácil de reverter um grupo de commits no repository 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 ^..`; do git revert -n $i; done 

    Isso reverterá um grupo de commits que você precisa, mas deixará todas as mudanças em sua tree 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 ^..`; do git revert --no-edit -s $i; done 

    Por exemplo, se você tem uma tree 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 

    Nenhum deles funcionou para mim, então eu tive 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 

    Trabalhou como um encanto 🙂

    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