Eu preciso abrir e eliminar um commit “intermediário” em meu branch master. Como eu posso fazer isso?

94

Por exemplo, no seguinte branch master, preciso eliminar apenas o commit af5c7bf16e6f04321f966b4231371b21475bc4da, que é o segundo devido ao rebase anterior:

commit 60b413512e616997c8b929012cf9ca56bf5c9113
Author: Luca G. Soave <[email protected]>
Date:   Tue Apr 12 23:50:15 2011 +0200

    add generic config/initializers/omniauth.example.rb

commit af5c7bf16e6f04321f966b4231371b21475bc4da
Author: Luca G. Soave <[email protected]>
Date:   Fri Apr 22 00:15:50 2011 +0200

    show github user info if logged

commit e6523efada4d75084e81971c4dc2aec621d45530
Author: Luca G. Soave <[email protected]>
Date:   Fri Apr 22 17:20:48 2011 +0200

    add multiple .container at blueprint layout

commit 414ceffc40ea4ac36ca68e6dd0a9ee97e73dee22
Author: Luca G. Soave <[email protected]>
Date:   Thu Apr 21 19:55:57 2011 +0200

    add %h1 Fantastic Logo + .right for 'Sign in with Github'

Eu preciso manter

  • o primeiro commit 60b413512e616997c8b929012cf9ca56bf5c9113,
  • o terceiro commit e6523efada4d75084e81971c4dc2aec621d45530 e
  • o último commit 414ceffc40ea4ac36ca68e6dd0a9ee97e73dee22

"jogando fora" apenas o Segundo commit af5c7bf16e6f04321f966b4231371b21475bc4da

Como eu posso fazer isso? Agradeço antecipadamente Luca

Luca G. Soave
fonte

Respostas:

98

Rebase ou reverter são as opções. Rebase irá realmente remover o commit do histórico, então vai parecer que o segundo commit nunca existiu. Isso será um problema se você enviar o branch master para qualquer outro repositório. Se você tentar fazer push após um rebase neste caso, o git apresentará um erro de rejeição de mesclagens sem avanço rápido .

Reverter é a solução correta quando a filial foi compartilhada com outros repositórios. git revert af5c7bf16fará um novo commit que simplesmente reverte as mudanças que af5c7bf16 introduziu. Dessa forma, o histórico não é reescrito, você mantém um registro claro do erro e outros repositórios aceitarão o push.

Esta é uma boa maneira de apagar: git rebase -i <commit>^ Isso leva você ao commit antes daquele que você deseja remover. O editor interativo mostrará uma lista de todos os commits até aquele ponto. Você pode escolher, esmagar, etc. Neste caso, remova a linha do commit que deseja apagar e salve o arquivo. Rebase terminará seu trabalho.

JCotton
fonte
2
Caso eu escolha Rebase, qual é o commit certo para rebase? Preciso largar só o segundo ...
Luca G. Soave
@ BBJ3 Veja a resposta de mipadi.
Prajwal Dhatwalia
32

Se rebase for uma opção, você pode rebase e simplesmente descartá-lo:

$ git rebase -i 414ceffc^

Se o rebase não for uma opção, você pode apenas revertê-lo:

$ git revert af5c7bf16
mipadi
fonte
se eu obtiver por "git rebase 414ceffc" que é o mais antigo quarto commit, não vou perder também o terceiro e6523 e o primeiro 60b41?
Luca G. Soave,
3
@Luca G. Soave: Você só "perde" um commit se disser especificamente git rebasepara descartá-lo (executando rebaseno modo interativo e removendo sua entrada).
mipadi
Obrigado mipadi, eu dou meu voto a JCotton essencialmente pela extensa explicação, mesmo que vocês dois tenham dito a mesma coisa ... obrigado novamente.
Luca G. Soave,
29

Apesar de todo o crédito que as respostas originais receberam aqui, não as encontrei para responder de forma satisfatória à pergunta. Se você se encontrar em uma situação em que precisa remover um commit, ou uma coleção de commits do meio da história, sugiro o seguinte:

  • Crie um novo branch fora da cabeça daquele que contém todos os commits e mude para ele.
  • Reverta a nova ramificação de volta ao ponto a partir do qual deseja iniciar uma nova base.
  • Então, (aqui está o ponto-chave) escolha os commits subsequentes que você realmente deseja aplicar depois disso do branch original para o novo, e pule os commits que você não quer mais (ou seja, aqueles que você está excluindo).
  • Se desejar, renomeie o branch original para algo que indique o código antigo e, em seguida, renomeie o novo branch como o original era chamado.
  • Finalmente, envie suas alterações para seu repositório remoto (se estiver usando um). Você provavelmente precisará usar um "push forçado". Se seus colaboradores tiverem problemas para puxar as revisões, pode ser mais fácil para eles simplesmente clonar o repositório novamente da fonte remota. De uma forma ou de outra, você provavelmente vai querer falar com eles se estiver arrancando commits do meio de sua história!

Aqui estão as informações sobre a seleção seletiva: O que significa a seleção seletiva de um commit com git?

Aqui estão algumas dicas sobre como fazer isso com o Tortoise Git (como acabei de fazer). É definitivamente mais fácil usar um utilitário gui para esses tipos de operações! Escolha cereja usando TortoiseGit

BuvinJ
fonte
6
Essa deveria ter sido a melhor resposta!
MadOgre
Ótima maneira de usar o cherry pick, essa solução é ainda melhor quando você deseja "pular" mais de um commit.
Johnny Willer
Não abordando a questão original. Embora a solução sugerida funcione, é muito mais demorada / complicada e o IMO não traz nenhum benefício. Se o ramo já foi empurrado (e usado em estado selvagem), a estratégia de reversão é provavelmente a resposta. Se não, eu usaria rebase interativo e removendo o commit ofensivo. Se ele foi empurrado, mas sabemos que não é usado por ninguém, você ainda pode fazer um rebase seguido por um push forçado.
raduw