Como remover permanentemente algumas confirmações da ramificação remota

490

Eu sei que reescrever a história é ruim yada yada.

Mas como remover permanentemente poucas confirmações da ramificação remota?

Arnis Lapsa
fonte
96
Sim, é muito ruim, yada yada, mas por algum motivo eu preciso favorito.
James Morris
44
Eu sei que isso é estúpido, mas às vezes acontece uma merda - como testar logons e usar senhas de texto sem formatação no seu código, que são credenciais reais de login. E
opa
6
credenciais reais Login .. Sim me lembra de alguns whoopos
Olá Universo
1
Possível duplicado de commits DELETE FROM uma filial na Git
wjandrea
2
Estou tão cansado desse discurso acadêmico sobre como isso é perigoso e como nunca deve ser feito yada yada. Há momentos em que é muito melhor remover coisas da história do git e lidar com os conflitos / quebra de outros desenvolvedores. É realmente assim tão simples. As pessoas que ignoram isso provavelmente nunca trabalharam fora de uma sala de aula.
crush

Respostas:

369

Você é git reset --hardsua filial local para remover as alterações da árvore e índice de trabalho e git push --forcesua filial local revisada para o controle remoto. ( outra solução aqui , envolvendo excluir a ramificação remota e pressioná-la novamente)

Esta resposta do SO ilustra o perigo de um comando desse tipo, especialmente se as pessoas dependem do histórico remoto para seus próprios repositórios locais.
Você precisa estar preparado para apontar pessoas para a seção RECUPERANDO DO UPSTREAM REBASE da git rebasepágina de manual


Com o Git 2.23 (agosto de 2019, nove anos depois), você usaria o novo comando git switch.
Ou seja: (substituagit switch -C mybranch origin/mybranch~n
n pelo número de confirmações para remover)

Isso restaurará o índice e a árvore de trabalho, como git reset --hardfaria.

VonC
fonte
Estranho. Parece que eu já tentei isso. Juntamente com algumas reformulações - funciona como um encanto. Obrigado.
Arnis Lapsa
7
@Arnis: perfeito então;) push --forceafastado
VonC
Usei o BFG Repo-Cleaner para me livrar de alguns dados confidenciais, que me deixaram com uma ramificação "sem nome" com o arquivo original, depois de redefinir para o último commit desejado e forçar a origem, tudo correu bem (:
Rodrirokr
Observe que o URL do commit ainda está ativo (pelo menos por algum tempo); portanto, se alguém tiver o URL do commit (você o deu a Deus sabe por que), ele poderá acessar o código.
Mosh Feu
1
@MoshFeu True: git gcnem sempre é executado o suficiente no lado remoto. Por exemplo, no GitHub: twitter.com/githubhelp/status/387926738161774592?lang=es
VonC
248

Apenas observe que last_working_commit_id, ao reverter um commit não ativo

git reset --hard <last_working_commit_id>

Portanto, não devemos redefinir para o commit_idque não queremos.

Então, com certeza, devemos enviar para o ramo remoto:

git push --force
hd84335
fonte
10
Resposta perfeita, elegante e mais simples. Eu só revertido para a última stabe cometer eu precisava tanto na remota e localmente
lauWM
2
Isso me levou a perder minhas alterações locais também. Eu não estava esperando isso. Mas meh, melhor do que confirmar sua senha pessoal para repo trabalhar.
Airwavezx
3
você pode salvar suas alterações locais com git stash .... fazer algumas coisas .... e git pop esconderijo (suas alterações locais estão de volta)
Montea
Isso me dá um erro como "remote: error: negando referências / cabeças / master que não são de avanço rápido (você deve puxar primeiro)"
Saurabhcdt
@ Airwavezx é o que git reset --harddeveria fazer.
Luke
146

Importante: Certifique-se de especificar quais ramificações no "git push -f" ou modificar inadvertidamente outras ramificações![*]

Existem três opções mostradas neste tutorial . Caso o link seja quebrado, deixarei as etapas principais aqui.

  1. Reverta a consolidação completa
  2. Excluir a última confirmação
  3. Excluir confirmação de uma lista

1 Reverta a consolidação completa

git revert dd61ab23

2 Exclua o último commit

git push <<remote>> +dd61ab23^:<<BRANCH_NAME_HERE>>

ou, se a filial estiver disponível localmente

git reset HEAD^ --hard
git push <<remote>> -f

onde + dd61 ... é o seu commit hash e git interpreta x ^ como o pai de x e + como um push forçado não-rápido.

3 Exclua a confirmação de uma lista

git rebase -i dd61ab23^

Isso abrirá e o editor exibirá uma lista de todas as confirmações. Exclua o que você deseja se livrar. Conclua a rebase e pressione a força para repo.

git rebase --continue
git push <remote_repo> <remote_branch> -f
reefaktor
fonte
5
Certifique-se de especificar quais ramificações em "git push <remote_repo> <remote_branch> -f" ou pode modificar inadvertidamente outras ramificações!
Nigel Sheridan-Smith
Somente as etapas 1 e 2 fizeram o trabalho e responderam à pergunta original. (Não execute a etapa 3)
Saad Benbouzid 11/03/16
Essas são opções para diferentes cenários, não etapas a serem seguidas. No meu caso, o rebase interativo (opção 3) fez o que eu estava procurando.
9788 Steve Jobswell
Eu tive que fazer o passo 3 embora. Por que você não deve executar este @Saad? Felizmente o meu << remoto >> era simplesmente o padrão 'origem' e <remote_branch> o padrão 'master'
Bart
30

Se você deseja excluir, por exemplo, as últimas 3confirmações, execute o seguinte comando para remover as alterações do sistema de arquivos (árvore de trabalho) e confirmar o histórico (índice) em sua filial local:

git reset --hard HEAD~3

Em seguida, execute o seguinte comando (na máquina local) para forçar a ramificação remota a reescrever seu histórico:

git push --force

Parabéns! Tudo feito!

Algumas notas:

Você pode recuperar o ID de confirmação desejado executando

git log

Então você pode substituir HEAD~Ncom <desired-commit-id>como esta:

git reset --hard <desired-commit-id>

Se você deseja manter as alterações no sistema de arquivos e apenas modificar o índice (histórico de consolidação), use --softsinalizador como git reset --soft HEAD~3. Então você tem a chance de verificar as alterações mais recentes e manter ou descartar todas ou parte delas. Neste último caso, runnig git statusmostra os arquivos alterados desde então <desired-commit-id>. Se você usar a --hardopção, git statusinformará que sua filial local é exatamente igual à remota. Se você não usar --hardnem --soft, o modo padrão será usado --mixed. Nesse modo,git help reset diz:

Redefine o índice, mas não a árvore de trabalho (ou seja, os arquivos alterados são preservados, mas não marcados para confirmação) e relata o que não foi atualizado.

Abdollah
fonte
10

Isso pode ser tarde demais, mas o que me ajudou é a opção "nuclear" que soa legal. Basicamente usando o comandofilter-branch você pode remover arquivos ou alterar algo em um grande número de arquivos ao longo de todo o seu histórico do git.

É melhor explicado aqui .

jansmolders86
fonte
9
Não é tarde demais. Pode tornar-se útil para andarilhos com problemas semelhantes :)
Arnis Lapsa
9

Simplificando a partir da resposta do pctroll, similarmente com base nesta postagem do blog .

# look up the commit id in git log or on github, e.g. 42480f3, then do
git checkout master
git checkout your_branch
git revert 42480f3
# a text editor will open, close it with ctrl+x (editor dependent)
git push origin your_branch
# or replace origin with your remote
ted.strauss
fonte
2
Funciona para mim, obrigado. E eu quero excluir permanentemente um commit (por exemplo, contém pwd) do histórico de filial remota, como fazer isso?
Smiles
1
Funciona para mim também, mas tenho a mesma pergunta que o Smiles, não quero que ninguém veja meu commit / rever na história .. como removo isso?
Roberto Rodriguez
4

Às vezes, a maneira mais fácil de corrigir esse problema é criar uma nova ramificação no local em que você sabe que o código é bom. Em seguida, você pode deixar o histórico de ramificação incorreto sozinho, caso precise escolher outros commits posteriormente. Isso também garante que você não perdeu nenhum histórico de consolidação.

Do seu ramo local errante:

git log

copie o hash de confirmação em que você deseja que o ramo esteja e saia do log git

git checkout theHashYouJustCopied
git checkout -b your_new_awesome_branch

Agora você tem um novo ramo do jeito que você deseja.

Se você também precisou manter um commit específico da ramificação incorreta que não está em sua nova ramificação, basta escolher a submissão específica de que precisa:

git checkout the_errant_branch
git log

Copie o hash de confirmação do que você precisa para entrar na ramificação válida e sair do log do git.

git checkout your_new_awesome_branch
git cherry-pick theHashYouJustCopied

Dê um tapinha nas costas.

Douglas.Sesar
fonte
muito fácil e quem se importa se você tem um galho de pernas mortas? faça um novo ramo e termine com ele!
Andrew Fox
Esta é uma resposta verdadeiramente excelente. Fico feliz por ter feito isso em vez de escolher cereja ou manipular o ramo remoto.
Magnilex
0
 git reset --soft commit_id
 git stash save "message"
 git reset --hard commit_id
 git stash apply stash stash@{0}
 git push --force
pandorago
fonte