Eu estava trabalhando com um amigo em um projeto, e ele editou vários arquivos que não deveriam ter sido editados. De alguma forma, fundei o trabalho dele no meu, quando o puxei ou quando tentei apenas escolher os arquivos específicos que queria. Estou procurando e reproduzindo há um longo tempo, tentando descobrir como remover os commits que contêm as edições desses arquivos, parece haver um lance entre reverter e refazer, e não há exemplos simples e os Os documentos assumem que eu sei mais do que sei.
Então, aqui está uma versão simplificada da pergunta:
Dado o cenário a seguir, como faço para remover o commit 2?
$ mkdir git_revert_test && cd git_revert_test
$ git init
Initialized empty Git repository in /Users/josh/deleteme/git_revert_test/.git/
$ echo "line 1" > myfile
$ git add -A
$ git commit -m "commit 1"
[master (root-commit) 8230fa3] commit 1
1 files changed, 1 insertions(+), 0 deletions(-)
create mode 100644 myfile
$ echo "line 2" >> myfile
$ git commit -am "commit 2"
[master 342f9bb] commit 2
1 files changed, 1 insertions(+), 0 deletions(-)
$ echo "line 3" >> myfile
$ git commit -am "commit 3"
[master 1bcb872] commit 3
1 files changed, 1 insertions(+), 0 deletions(-)
O resultado esperado é
$ cat myfile
line 1
line 3
Aqui está um exemplo de como eu tenho tentado reverter
$ git revert 342f9bb
Automatic revert failed. After resolving the conflicts,
mark the corrected paths with 'git add <paths>' or 'git rm <paths>'
and commit the result.
git
commit
revert
cherry-pick
Joshua Cheek
fonte
fonte
Respostas:
O algoritmo que o Git usa ao calcular as diferenças a serem revertidas exige que
A definição de "adjacente" é baseada no número padrão de linhas de uma diferença de contexto, que é 3. Portanto, se 'myfile' fosse construído assim:
Então tudo funciona como esperado.
A segunda resposta foi muito interessante. Há um recurso que ainda não foi lançado oficialmente (embora esteja disponível no Git v1.7.2-rc2) chamado Revert Strategy. Você pode invocar o git assim:
e deve fazer um trabalho melhor para descobrir o que você quis dizer. Não sei qual é a lista de estratégias disponíveis, nem a definição de qualquer estratégia.
fonte
perl -p
é útil para escrever programas muito curtos (uma linha) que fazem um loop e passam sua entrada para a saída, semelhante ao sed.perl -i
é usado para editar arquivos no local .perl -e
é como enviar o código a ser avaliado .Existem quatro maneiras de fazer isso:
Maneira limpa, revertendo, mas mantenha em log a reversão:
De maneira severa, remova completamente apenas o último commit:
Nota: Evite
git reset --hard
, pois também descartará todas as alterações nos arquivos desde a última confirmação. Se--soft
não funcionar, tente--mixed
ou--keep
.Rebase (mostre o log das últimas 5 confirmações e exclua as linhas que você não deseja, ou reordene ou esmague várias confirmações em uma ou faça o que quiser, esta é uma ferramenta muito versátil):
E se um erro for cometido:
Rebase rápido: remova apenas uma confirmação específica usando seu ID:
Alternativas: você também pode tentar:
Ainda outra alternativa:
Como último recurso, se você precisar de total liberdade de edição do histórico (por exemplo, porque o git não permite editar o que você deseja), você pode usar este aplicativo de código aberto muito rápido : reposicionador .
Nota: é claro, todas essas alterações são feitas localmente,
git push
depois você deve aplicar as alterações no controle remoto. E caso seu repositório não queira remover a confirmação ("não é permitido o avanço rápido", o que acontece quando você deseja remover uma confirmação que você já enviou), você pode usargit push -f
para forçar as alterações.Nota2: se você estiver trabalhando em um ramo e precisar forçar o envio, evite absolutamente,
git push --force
pois isso pode sobrescrever outros ramos (se você tiver feito alterações neles, mesmo que seu checkout atual esteja em outro ramo). Preferem especificar sempre o ramo remoto quando você forçar impulso :git push --force origin your_branch
.fonte
git rebase -i HEAD~5
trabalhou para mim. Em seguida, removi as confirmações que não precisava e consegui resolver o problema em menos de 30 segundos. Obrigado.Aqui está uma solução fácil:
(Nota:
x
é o número de confirmações)após a execução, o arquivo do bloco de notas será aberto. Entre
drop
além do seu commit.Se você não conhece o Vim, basta clicar em cada paleta de palavras que deseja editar e pressionar a tecla "I" (para o modo de inserção). Quando terminar de digitar, pressione a tecla "esc" para sair do modo de inserção.
e pronto, basta sincronizar o painel do git e as alterações serão enviadas para o remoto.
Se o commit que você soltar já estava no controle remoto, você precisará forçar o push. Como --force é considerado prejudicial , use
git push --force-with-lease
.fonte
Sua escolha é entre
Você deve escolher (1) se a alteração incorreta foi detectada por mais alguém e (2) se o erro é limitado a uma ramificação não pressionada privada.
Git revert é uma ferramenta automatizada para fazer (1), cria um novo commit desfazendo algum commit anterior. Você verá o erro e a remoção no histórico do projeto, mas as pessoas que puxam do seu repositório não terão problemas quando atualizarem. Não está funcionando de maneira automatizada no seu exemplo, então você precisa editar 'myfile' (para remover a linha 2), fazer
git add myfile
egit commit
lidar com o conflito. Você terminará com quatro confirmações no seu histórico, com a confirmação 4 revertendo a confirmação 2.Se ninguém se importa que seu histórico seja alterado, você pode reescrevê-lo e remover o commit 2 (opção 2). A maneira mais fácil de fazer isso é usar
git rebase -i 8230fa3
. Isso o levará a um editor e você poderá optar por não incluir a confirmação incorreta removendo a confirmação (e mantendo "pick" ao lado das outras mensagens de confirmação. Leia as conseqüências de fazer isso .fonte
Approch 1
Primeiro obtenha o hash de confirmação (ex: 1406cd61) que você precisa reverter. correção simples estará abaixo do comando,
se você confirmar mais alterações relacionadas aos arquivos 1406cd61 após a confirmação 1406cd61, o comando Above simple não funcionará. Então você tem que seguir os passos abaixo, que é a escolha da cereja.
Approch 2
Siga as etapas abaixo, pois estamos usando o --force, você precisa ter direitos de administrador sobre o repositório git para fazer isso.
Etapa 1: encontre a confirmação antes da confirmação que você deseja remover
git log
Etapa 2: finalizar a compra que confirma
git checkout <commit hash>
Etapa 3: crie uma nova ramificação usando seu commit de checkout atual
git checkout -b <new branch>
Etapa 4: Agora você precisa adicionar a confirmação após a confirmação removida
git cherry-pick <commit hash>
Etapa 5: Agora repita a Etapa 4 para todas as outras confirmações que deseja manter.
Etapa 6: depois que todas as confirmações foram adicionadas à sua nova filial e foram confirmadas. Verifique se tudo está no estado correto e funcionando como planejado. Verifique tudo que foi confirmado:
git status
Etapa 7: mude para o seu ramo quebrado
git checkout <broken branch>
Etapa 8: Agora execute uma redefinição definitiva no ramo quebrado para o commit antes do que você deseja remover
git reset --hard <commit hash>
Etapa 9: mesclar sua ramificação fixa nessa ramificação
git merge <branch name>
Etapa 10: envie as alterações mescladas de volta à origem. AVISO: Isso substituirá o repositório remoto!
git push --force origin <branch name>
Você pode executar o processo sem criar uma nova ramificação, substituindo as etapas 2 e 3 pela etapa 8 e não executando as etapas 7 e 9.
fonte
Você pode remover confirmações indesejadas com
git rebase
. Digamos que você incluiu algumas confirmações da ramificação de tópicos de um colega de trabalho em sua ramificação de tópicos, mas depois decida que não deseja essas confirmações.Nesse ponto, seu editor de texto abrirá a exibição de rebase interativa. Por exemplo
Se a recuperação não tiver êxito, exclua a ramificação temporária e tente outra estratégia. Caso contrário, continue com as seguintes instruções.
Se você estiver enviando a ramificação do tópico para um controle remoto, talvez seja necessário forçar o envio, pois o histórico de confirmação foi alterado. Se outros estiverem trabalhando no mesmo ramo, avise-os.
fonte
De outras respostas aqui, eu fiquei meio confuso com como
git rebase -i
poderia ser usado para remover uma confirmação, então espero que seja correto anotar meu caso de teste aqui (muito semelhante ao OP).Aqui está um
bash
script que você pode colar para criar um repositório de teste na/tmp
pasta:Neste ponto, temos um
file.txt
com este conteúdo:Nesse ponto, HEAD está no 5º commit, HEAD ~ 1 seria o quarto - e HEAD ~ 4 seria o 1º commit (portanto, HEAD ~ 5 não existiria). Digamos que queremos remover o terceiro commit - podemos emitir este comando no
myrepo_git
diretório:( Observe que
git rebase -i HEAD~5
resulta em "fatal: precisava de uma única revisão; HEAD ~ 5 upstream inválido". ) Um editor de texto (veja a captura de tela na resposta do @Dennis ' ) será aberto com o seguinte conteúdo:Portanto, obtemos todos os commits desde (mas não incluindo ) nosso pedido HEAD ~ 4. Exclua a linha
pick 448c212 3rd git commit
e salve o arquivo; você receberá esta resposta degit rebase
:Neste ponto, abra myrepo_git /
folder/file.txt
em um editor de texto; você verá que foi modificado:Basicamente,
git
vê que quando HEAD chegou ao segundo commit, havia conteúdo deaaaa
+bbbb
; e, em seguida, possui um patch adicionadocccc
+dddd
que não sabe como anexar ao conteúdo existente.Portanto, aqui
git
não é possível decidir por você - é você quem deve tomar uma decisão: removendo o terceiro commit, você mantém as alterações introduzidas por ele (aqui, a linhacccc
) - ou não. Caso contrário, remova as linhas extras - incluindo acccc
-folder/file.txt
usando um editor de texto, para que fique assim:... e depois salve
folder/file.txt
. Agora você pode emitir os seguintes comandos nomyrepo_git
diretório:Ah - por isso, a fim de marca que nós resolvemos o conflito, que deve
git add
ofolder/file.txt
, antes de fazergit rebase --continue
:Aqui um editor de texto é aberto novamente, mostrando a linha
4th git commit
- aqui temos a chance de alterar a mensagem de confirmação (que neste caso pode ser significativamente alterada para4th (and removed 3rd) commit
ou similar). Digamos que você não queira - então basta sair do editor de texto sem salvar; Depois de fazer isso, você receberá:Neste ponto, agora você tem um histórico como este (que você também pode inspecionar com digamos
gitk .
ou outras ferramentas) do conteúdofolder/file.txt
(com, aparentemente, timestamps inalterados dos commits originais):E se anteriormente, decidimos manter a linha
cccc
(o conteúdo do terceiro commit do git que removemos), teríamos:Bem, esse era o tipo de leitura que eu esperava ter encontrado, para começar a entender como
git rebase
funciona em termos de exclusão de confirmações / revisões; espero que ajude outras pessoas também ...fonte
Portanto, parece que o commit incorreto foi incorporado em um commit de mesclagem em algum momento. Seu commit de mesclagem já foi realizado? Se sim, você vai querer usar
git revert
; você terá que cerrar os dentes e resolver os conflitos. Se não, você pode, se possível , refazer ou reverter, mas pode fazê-lo antes da consolidação da mesclagem e refazer a mesclagem.Não há muita ajuda que possamos oferecer para o primeiro caso, realmente. Depois de tentar reverter e descobrir que o automático falhou, você deve examinar os conflitos e corrigi-los adequadamente. Esse é exatamente o mesmo processo que a correção de conflitos de mesclagem; você pode usar
git status
para ver onde estão os conflitos, editar os arquivos não mesclados, encontrar os pedaços conflitantes, descobrir como resolvê-los, adicionar os arquivos conflitantes e finalmente confirmar. Se você usargit commit
sozinho (não-m <message>
), a mensagem exibida em seu editor deve ser a mensagem de modelo criada porgit revert
; você pode adicionar uma observação sobre como você corrigiu os conflitos, salvar e sair para confirmar.Para o segundo caso, corrigindo o problema antes da mesclagem, existem duas subcasas, dependendo se você fez mais trabalho desde a mesclagem. Caso contrário, você pode simplesmente
git reset --hard HEAD^
interromper a mesclagem, reverter e refazer a mesclagem. Mas acho que sim. Então, você acabará fazendo algo assim:git rebase -i <something before the bad commit> <temporary branch>
para remover a consolidação incorreta)git rebase --onto <temporary branch> <old merge commit> <real branch>
fonte
Então, você fez algum trabalho e pressionou, vamos chamá-los de commit A e B. Seu colega de trabalho também deu certo, confirma C e D. Você mesclou o trabalho de seus colegas no seu (mesclar commit E), depois continuou trabalhando, confirmou que, (comprometa F) e descobriu que seu colega de trabalho mudou algumas coisas que ele não deveria ter.
Portanto, seu histórico de consolidação é assim:
Você realmente quer se livrar de C, D e D '. Como você diz que mesclou o trabalho de seus colegas com o seu, esses commits já estão "disponíveis", portanto, remover os commits usando, por exemplo, git rebase é um não-não. Acredite, eu tentei.
Agora, vejo duas maneiras de sair:
se você ainda não enviou E e F ao seu colega de trabalho ou a qualquer outra pessoa (normalmente seu servidor de "origem"), ainda pode removê-los do histórico por enquanto. Este é o seu trabalho que você deseja salvar. Isso pode ser feito com um
(substitua D 'pelo hash de confirmação real que você pode obter de um
git log
Neste ponto, as confirmações E e F desaparecem e as alterações não são confirmadas na área de trabalho local novamente. Nesse ponto, eu os movia para um galho ou os transformava em um patch e o guardava para mais tarde. Agora, reverta o trabalho do seu colega de trabalho, automaticamente com um
git revert
ou manualmente. Quando você tiver feito isso, repita o seu trabalho em cima disso. Você pode ter conflitos de mesclagem, mas pelo menos eles estarão no código que você escreveu, em vez do código do seu colega de trabalho.Se você já executou o trabalho que fez após o comprometimento do colega de trabalho, ainda pode tentar obter um "patch reverso" manualmente ou usando-o
git revert
, mas como o seu trabalho está "no caminho", por assim dizer, você provavelmente obterá mais conflitos de mesclagem e mais confusos. Parece que foi nisso que você acabou ...fonte
git revert --strategy resolve se commit é uma mesclagem: use git revert --strategy resolve -m 1
fonte