Introdução: Você tem 5 soluções disponíveis
O pôster original declara:
Confirmei acidentalmente um arquivo indesejado ... para o meu repositório há vários commits atrás ... Quero excluir completamente o arquivo do histórico do repositório.
É possível reescrever o histórico de alterações, que filename.orig
nunca foi adicionado ao repositório em primeiro lugar?
Existem várias maneiras de remover completamente o histórico de um arquivo do git:
- Alterações confirmadas.
- Redefinições rígidas (possivelmente mais uma rebase).
- Rebase não interativo.
- Reestruturações interativas.
- Filtrar filiais.
No caso do pôster original, alterar o commit não é realmente uma opção por si só, já que ele fez vários commit adicionais depois, mas por uma questão de completude, também explicarei como fazê-lo, para qualquer pessoa que apenas deseje alterar seu commit anterior.
Observe que todas essas soluções envolvem alterar / reescrever o histórico / confirmações de uma maneira ou outra, portanto, qualquer pessoa com cópias antigas das confirmações precisará fazer um trabalho extra para sincronizar novamente sua história com a nova história.
Solução 1: Alterando confirmações
Se você acidentalmente fez uma alteração (como adicionar um arquivo) no commit anterior e não deseja mais que o histórico dessa alteração exista, basta alterar o commit anterior para remover o arquivo:
git rm <file>
git commit --amend --no-edit
Solução 2: reinicialização total (possivelmente mais uma rebase)
Como a solução 1, se você apenas deseja se livrar do seu commit anterior, também tem a opção de fazer uma redefinição definitiva do pai:
git reset --hard HEAD^
Esse comando reinicializará sua ramificação para o 1º compromisso pai anterior .
No entanto , se, como o pôster original, você fez várias confirmações após a confirmação em que deseja desfazer a alteração, ainda é possível usar redefinições rígidas para modificá-la, mas isso também envolve o uso de uma nova base. Aqui estão as etapas que você pode usar para alterar um commit mais adiante no histórico:
# Create a new branch at the commit you want to amend
git checkout -b temp <commit>
# Amend the commit
git rm <file>
git commit --amend --no-edit
# Rebase your previous branch onto this new commit, starting from the old-commit
git rebase --preserve-merges --onto temp <old-commit> master
# Verify your changes
git diff master@{1}
Solução 3: Rebase não interativo
Isso funcionará se você quiser remover completamente uma confirmação do histórico:
# Create a new branch at the parent-commit of the commit that you want to remove
git branch temp <parent-commit>
# Rebase onto the parent-commit, starting from the commit-to-remove
git rebase --preserve-merges --onto temp <commit-to-remove> master
# Or use `-p` insteda of the longer `--preserve-merges`
git rebase -p --onto temp <commit-to-remove> master
# Verify your changes
git diff master@{1}
Solução 4: rebotes interativos
Esta solução permitirá que você realize o mesmo que as soluções nº 2 e nº 3, ou seja, modifique ou remova confirmações mais antigas do que a confirmação imediatamente anterior, de modo que a solução que você escolher usar depende de você. Os rebotes interativos não são adequados para refazer centenas de confirmações, por motivos de desempenho, portanto, eu usaria os rebotes não interativos ou a solução de ramificação do filtro (veja abaixo) nesses tipos de situações.
Para iniciar o rebase interativo, use o seguinte:
git rebase --interactive <commit-to-amend-or-remove>~
# Or `-i` instead of the longer `--interactive`
git rebase -i <commit-to-amend-or-remove>~
Isso fará com que o git rebobine o histórico de consolidação novamente para o pai da consolidação que você deseja modificar ou remover. Em seguida, ele apresentará uma lista dos commit rebobinados na ordem inversa, independentemente do editor que o git estiver configurado para usar (este é o Vim por padrão):
pick 00ddaac Add symlinks for executables
pick 03fa071 Set `push.default` to `simple`
pick 7668f34 Modify Bash config to use Homebrew recommended PATH
pick 475593a Add global .gitignore file for OS X
pick 1b7f496 Add alias for Dr Java to Bash config (OS X)
A confirmação que você deseja modificar ou remover estará no topo desta lista. Para removê-lo, basta excluir sua linha na lista. Caso contrário, substitua "pegar" com "editar" no 1 st linha, assim:
edit 00ddaac Add symlinks for executables
pick 03fa071 Set `push.default` to `simple`
Em seguida, insira git rebase --continue
. Se você optou por remover o commit por completo, então tudo o que você precisa fazer (além da verificação, consulte a etapa final desta solução). Se, por outro lado, você quiser modificar o commit, o git reaplicará o commit e pausará o rebase.
Stopped at 00ddaacab0a85d9989217dd9fe9e1b317ed069ac... Add symlinks
You can amend the commit now, with
git commit --amend
Once you are satisfied with your changes, run
git rebase --continue
Neste ponto, você pode remover o arquivo e alterar a confirmação e, em seguida, continue a nova atualização:
git rm <file>
git commit --amend --no-edit
git rebase --continue
É isso aí. Como etapa final, se você modificou a confirmação ou a removeu completamente, é sempre uma boa ideia verificar se nenhuma outra alteração inesperada foi feita em sua ramificação, diferenciando-a do estado anterior à rebase:
git diff master@{1}
Solução 5: filtrando ramificações
Por fim, essa solução é melhor se você deseja eliminar completamente todos os vestígios da existência de um arquivo do histórico, e nenhuma das outras soluções é adequada à tarefa.
git filter-branch --index-filter \
'git rm --cached --ignore-unmatch <file>'
Isso removerá <file>
de todas as confirmações, iniciando na confirmação raiz. Se, em vez disso, você deseja apenas reescrever o intervalo de confirmação HEAD~5..HEAD
, pode passar isso como um argumento adicional para filter-branch
, como apontado
nesta resposta :
git filter-branch --index-filter \
'git rm --cached --ignore-unmatch <file>' HEAD~5..HEAD
Novamente, após a filter-branch
conclusão, geralmente é uma boa idéia verificar se não há outras alterações inesperadas diferenciando sua ramificação com seu estado anterior antes da operação de filtragem:
git diff master@{1}
Alternativa para ramificação de filtro: BFG Repo Cleaner
Ouvi dizer que a ferramenta BFG Repo Cleaner é executada mais rapidamente do que git filter-branch
, portanto, convém verificar isso como uma opção. É até mencionado oficialmente na documentação da ramificação do filtro como uma alternativa viável:
O git-filter-branch permite que você faça reescritas complexas com scripts do shell do seu histórico do Git, mas você provavelmente não precisará dessa flexibilidade se estiver simplesmente removendo dados indesejados, como arquivos grandes ou senhas. Para essas operações, você pode considerar o BFG Repo-Cleaner , uma alternativa baseada em JVM para git-filter-branch, geralmente pelo menos 10 a 50x mais rápido para esses casos de uso e com características bastante diferentes:
Qualquer versão específica de um arquivo é limpa exatamente uma vez . O BFG, diferentemente do git-filter-branch, não oferece a oportunidade de manipular um arquivo de maneira diferente, com base em onde ou quando ele foi confirmado no seu histórico. Essa restrição oferece os principais benefícios de desempenho do The BFG e é adequada para a tarefa de limpeza de dados inválidos - você não se importa onde estão os dados incorretos, apenas deseja que eles sejam removidos .
Por padrão, o BFG aproveita ao máximo as máquinas com vários núcleos, limpando as árvores de arquivos de confirmação em paralelo. O git-filter-branch limpa as confirmações sequencialmente (isto é, de uma única thread), embora seja
possível escrever filtros que incluam seu próprio paralelismo, nos scripts executados em cada confirmação.
As opções de comando são muito mais restritiva do que git branch-filtro, e dedicou apenas às tarefas de remoção indesejada data- por exemplo: --strip-blobs-bigger-than 1M
.
Recursos adicionais
- Pro Git § 6.4 Git Tools - Reescrevendo o Histórico .
- Manual do git-filter-branch (1) .
- Manual do git-commit (1) .
- Manual do git-reset (1) .
- Manual do git-rebase (1) .
- O BFG Repo Cleaner (veja também esta resposta do próprio criador ).