Como remover / excluir um arquivo grande do histórico de consolidação no repositório Git?

708

Ocasionalmente, eu soltava um DVD-rip em um projeto de site, então descuidadamente git commit -a -m ...e, zap, o repositório foi inchado por 2,2 shows. Na próxima vez que fiz algumas edições, excluí o arquivo de vídeo e confirmei tudo, mas o arquivo compactado ainda está lá no repositório, no histórico.

Eu sei que posso iniciar ramificações a partir dessas confirmações e refazer uma ramificação para outra. Mas o que devo fazer para mesclar as duas confirmações para que o arquivo grande não apareça no histórico e seja limpo no procedimento de coleta de lixo?

culebrón
fonte
9
Este artigo deve ajudá-lo a help.github.com/removing-sensitive-data
MBO
1
Observe que, se seu arquivo grande estiver em um subdiretório, será necessário especificar o caminho relativo completo.
23415 Johan Johan
1
Também relacionado help.github.com/en/articles/…
frederj 27/05
Muitas respostas abaixo consideram o BFG mais fácil do que git filter-branch, mas achei o oposto verdadeiro.
2540625

Respostas:

605

Use o BFG Repo-Cleaner , uma alternativa mais simples e rápida para o git-filter-branchprojetado especificamente para remover arquivos indesejados do histórico do Git.

Siga cuidadosamente as instruções de uso , a parte principal é exatamente isso:

$ java -jar bfg.jar --strip-blobs-bigger-than 100M my-repo.git

Todos os arquivos com mais de 100 MB de tamanho (que não estão no seu commit mais recente ) serão removidos do histórico do seu repositório Git. Você pode usar git gcpara limpar os dados mortos:

$ git gc --prune=now --aggressive

O BFG é geralmente pelo menos 10-50x mais rápido que a execução git-filter-branche geralmente mais fácil de usar.

Divulgação completa: sou o autor do BFG Repo-Cleaner.

Roberto Tyley
fonte
4
@tony Vale a pena repetir todo o procedimento de clonagem e limpeza para ver se a mensagem solicitando a repetição ocorre, mas é quase certo que o servidor remoto está configurado para rejeitar atualizações que não são de avanço rápido (ou seja, está configurado para impedir você de perder a história - que é exatamente o que você deseja fazer). Você precisa alterar essa configuração no controle remoto ou, na sua falta, enviar o histórico atualizado do repositório para um novo repositório em branco.
Roberto Tyley 23/02
1
@RobertoTyley Thanks. Eu tentei 3 vezes diferentes e todos resultaram com a mesma mensagem. Então, eu também acho que você está certo sobre o servidor remoto ser configurado para rejeitar as atualizações que não são de avanço rápido. Considerarei apenas transferir o repo atualizado para um repo novo. Obrigado!
23414 Tony
7
@RobertoTyley Perfeito, você economiza meu tempo, muito obrigado. A propósito, talvez você deva fazer git push --forceapós as etapas, caso contrário, o repo remoto ainda não foi alterado.
li2 22/07/2015
3
+1 para adicionar git push --force. Também digno de nota: forçar push pode não ser permitido pelo controle remoto (gitlab.com não, por padrão. Tinha que "desproteger" a ramificação).
MatrixManAtYrService 10/09
25
Eu acho que o jargão de Trump que a ferramenta gera é um pouco demais.
Chris
564

O que você quer fazer é altamente perturbador se você tiver publicado o histórico para outros desenvolvedores. Consulte "Recuperando da Rebase Upstream" na git rebasedocumentação para obter as etapas necessárias após reparar seu histórico.

Você tem pelo menos duas opções: git filter-branche um rebase interativo, ambos explicados abaixo.

Usando git filter-branch

Eu tive um problema semelhante com dados volumosos de teste binário de uma importação do Subversion e escrevi sobre a remoção de dados de um repositório git .

Digamos que seu histórico do git seja:

$ git lola --name-status
* f772d66 (HEAD, master) Login page
| A     login.html
* cb14efd Remove DVD-rip
| D     oops.iso
* ce36c98 Careless
| A     oops.iso
| A     other.html
* 5af4522 Admin page
| A     admin.html
* e738b63 Index
  A     index.html

Observe que git lolaé um alias não padrão, mas altamente útil. Com a --name-statusopção, podemos ver modificações na árvore associadas a cada confirmação.

No commit "descuidado" (cujo nome do objeto SHA1 é ce36c98), o arquivo oops.isoé o rasgo do DVD adicionado por acidente e removido no próximo commit, cb14efd. Usando a técnica descrita na postagem de blog acima mencionada, o comando a ser executado é:

git filter-branch --prune-empty -d /dev/shm/scratch \
  --index-filter "git rm --cached -f --ignore-unmatch oops.iso" \
  --tag-name-filter cat -- --all

Opções:

  • --prune-emptyremove confirmações que ficam vazias ( ou seja , não alteram a árvore) como resultado da operação do filtro. No caso típico, esta opção produz um histórico mais limpo.
  • -dnomeia um diretório temporário que ainda não existe para usar na construção do histórico filtrado. Se você estiver executando em uma distribuição moderna do Linux, especificar uma árvore /dev/shmresultará em uma execução mais rápida .
  • --index-filteré o evento principal e é executado no índice em cada etapa do histórico. Você deseja remover oops.isoonde quer que seja encontrado, mas não está presente em todas as confirmações. O comando git rm --cached -f --ignore-unmatch oops.isoexclui o DVD-rip quando está presente e não falha caso contrário.
  • --tag-name-filterdescreve como reescrever nomes de tags. Um filtro de caté a operação de identidade. Seu repositório, como o exemplo acima, pode não ter nenhuma tag, mas eu incluí esta opção para generalidade total.
  • -- especifica o final das opções para git filter-branch
  • --alla seguir --é uma abreviação para todas as referências. Seu repositório, como a amostra acima, pode ter apenas uma referência (mestre), mas eu incluí esta opção para generalidade total.

Depois de alguma agitação, a história é agora:

$ git lola --name-status
* 8e0a11c (HEAD, master) Login page
| A     login.html
* e45ac59 Careless
| A     other.html
|
| * f772d66 (refs/original/refs/heads/master) Login page
| | A   login.html
| * cb14efd Remove DVD-rip
| | D   oops.iso
| * ce36c98 Careless
|/  A   oops.iso
|   A   other.html
|
* 5af4522 Admin page
| A     admin.html
* e738b63 Index
  A     index.html

Observe que a nova confirmação "descuidado" é adicionada apenas other.htmle que a confirmação "Remover DVD-rip" não está mais na ramificação principal. A ramificação rotulada refs/original/refs/heads/mastercontém as confirmações originais caso você cometa um erro. Para removê-lo, siga as etapas em "Lista de verificação para reduzir um repositório".

$ git update-ref -d refs/original/refs/heads/master
$ git reflog expire --expire=now --all
$ git gc --prune=now

Para uma alternativa mais simples, clone o repositório para descartar os bits indesejados.

$ cd ~/src
$ mv repo repo.old
$ git clone file:///home/user/src/repo.old repo

O uso de um file:///...URL de clone copia objetos em vez de criar apenas links físicos.

Agora sua história é:

$ git lola --name-status
* 8e0a11c (HEAD, master) Login page
| A     login.html
* e45ac59 Careless
| A     other.html
* 5af4522 Admin page
| A     admin.html
* e738b63 Index
  A     index.html

Os nomes dos objetos SHA1 para as duas primeiras confirmações ("Índice" e "Página de administração") permaneceram os mesmos porque a operação de filtro não modificou essas confirmações. “Careless” perdido oops.isoe “página de login” tem um novo pai, para que seus SHA1s fez a mudança.

Rebase interativo

Com um histórico de:

$ git lola --name-status
* f772d66 (HEAD, master) Login page
| A     login.html
* cb14efd Remove DVD-rip
| D     oops.iso
* ce36c98 Careless
| A     oops.iso
| A     other.html
* 5af4522 Admin page
| A     admin.html
* e738b63 Index
  A     index.html

você deseja remover oops.isodo “Careless” como se nunca o tivesse adicionado e, em seguida, “Remove DVD-rip” é inútil para você. Portanto, nosso plano para uma reestruturação interativa é manter a "Página do administrador", editar "Descuidado" e descartar "Remover o rasgo do DVD".

A execução $ git rebase -i 5af4522inicia um editor com o seguinte conteúdo.

pick ce36c98 Careless
pick cb14efd Remove DVD-rip
pick f772d66 Login page

# Rebase 5af4522..f772d66 onto 5af4522
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#  x, exec = run command (the rest of the line) using shell
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

Executando nosso plano, modificamos para

edit ce36c98 Careless
pick f772d66 Login page

# Rebase 5af4522..f772d66 onto 5af4522
# ...

Ou seja, excluímos a linha com “Remove DVD-rip” e alteramos a operação em “Careless” para editmelhor que para pick.

Ao sair do editor, a opção salvar é exibida no prompt de comando com a seguinte mensagem.

Stopped at ce36c98... Careless
You can amend the commit now, with

        git commit --amend

Once you are satisfied with your changes, run

        git rebase --continue

Como a mensagem nos diz, estamos no commit "descuidado" que queremos editar, então executamos dois comandos.

$ git rm --cached oops.iso
$ git commit --amend -C HEAD
$ git rebase --continue

O primeiro remove o arquivo incorreto do índice. O segundo modifica ou altera “Careless” para ser o índice atualizado e -C HEADinstrui o git a reutilizar a antiga mensagem de confirmação. Por fim, git rebase --continueprossegue com o restante da operação de rebase.

Isso fornece um histórico de:

$ git lola --name-status
* 93174be (HEAD, master) Login page
| A     login.html
* a570198 Careless
| A     other.html
* 5af4522 Admin page
| A     admin.html
* e738b63 Index
  A     index.html

qual é o que você quer.

Greg Bacon
fonte
4
Por que não consigo enviar por push ao usar o git filter-branch, falhei ao enviar algumas referências para '[email protected]: product / myproject.git' Para evitar que você perca histórico, as atualizações de avanço rápido foram rejeitadas Mesclar o controle remoto muda antes de pressionar novamente.
Agung Prasetyo
11
Adicione a opção -f(ou --force) ao seu git pushcomando: “Normalmente, o comando se recusa a atualizar uma ref remota que não é um ancestral da ref local usada para substituí-la. Este sinalizador desativa a verificação. Isso pode fazer com que o repositório remoto perca confirmações; use-o com cuidado. ”
Greg Bacon
5
Esta é uma resposta maravilhosamente completa que explica o uso do git-filter-branch para remover arquivos grandes indesejados da história, mas vale a pena notar que desde que Greg escreveu sua resposta, o BFG Repo-Cleaner foi lançado, o que geralmente é mais rápido e fácil de usar. use - veja minha resposta para detalhes.
Roberto Tyley
1
Depois de executar um dos procedimentos acima, o repositório remoto (no GitHub) NÃO exclui o arquivo grande. Somente o local faz. Eu forço push e nada. o que estou perdendo?
Azatar 13/05
1
isso também funciona em dirs. ... "git rm --cached -rf --ignore-unmatch path/to/dir"...
rynop
198

Por que não usar este comando simples, mas poderoso?

git filter-branch --tree-filter 'rm -f DVD-rip' HEAD

A --tree-filteropção executa o comando especificado após cada checkout do projeto e, em seguida, confirma novamente os resultados. Nesse caso, você remove um arquivo chamado DVD-rip de todos os instantâneos, existindo ou não.

Se você souber qual commit introduziu o arquivo enorme (por exemplo, 35dsa2), poderá substituir HEAD por 35dsa2..HEAD para evitar a reescrita de histórico demais, evitando assim confirmações divergentes, se você ainda não o enviou. Este comentário de cortesia de @ alpha_989 parece importante demais para ser deixado de lado aqui.

Veja este link .

Gary Gauh
fonte
3
Esta é uma boa solução! Eu criei uma essência que tem um script python para listar os arquivos e o git cmd que irá apagar o arquivo que deseja limpar gist.github.com/ariv3ra/16fd94e46345e62cfcbf
punkdata
5
Muito melhor do que bfg. Eu era incapaz de arquivo limpo de um git com bfg, mas este comando ajudou
podarok
4
Isso é ótimo. Apenas uma observação para outras pessoas de que você precisará fazer isso por ramificação se o arquivo grande estiver em várias ramificações.
James
2
No Windows, obtive o fatal: bad revision 'rm'que corrigi usando em "vez de '. Comando geral:git filter-branch --force --index-filter "git rm --cached -r --ignore-unmatch oops.iso" --prune-empty --tag-name-filter cat -- --all
marcotama 4/16
2
Se você souber commitonde colocou o arquivo (digamos 35dsa2), poderá substituí-lo HEADpor 35dsa2..HEAD. tree-filteré muito mais lento index-filterque o normal e não tentará fazer check-out de todos os commits e reescrevê-los. se você usar HEAD, ele tentará fazer isso.
alpha_989
86

(A melhor resposta que eu já vi para esse problema é: https://stackoverflow.com/a/42544963/714112 , copiada aqui, pois esse segmento aparece alto nos rankings de pesquisa do Google, mas o outro não.

🚀 Um one-liner shell incrivelmente rápido 🚀

Esse script de shell exibe todos os objetos de blob no repositório, classificados do menor para o maior.

Para o meu repositório de amostras, ele foi executado 100 vezes mais rápido que os outros encontrados aqui.
No meu confiável sistema Athlon II X4, ele lida com o repositório Linux Kernel com seus 5.622.155 objetos em pouco mais de um minuto .

O Script Base

git rev-list --objects --all \
| git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' \
| awk '/^blob/ {print substr($0,6)}' \
| sort --numeric-sort --key=2 \
| cut --complement --characters=13-40 \
| numfmt --field=2 --to=iec-i --suffix=B --padding=7 --round=nearest

Quando você executa o código acima, você obtém uma boa saída legível por humanos como esta:

...
0d99bb931299  530KiB path/to/some-image.jpg
2ba44098e28f   12MiB path/to/hires-image.png
bd1741ddce0d   63MiB path/to/some-video-1080p.mp4

🚀 Remoção rápida de arquivo 🚀

Suponha que você deseje remover os arquivos ae, a bpartir de cada confirmação acessível HEAD, você pode usar este comando:

git filter-branch --index-filter 'git rm --cached --ignore-unmatch a b' HEAD
Sridhar Sarnobat
fonte
3
Se o seu repo tem todas as tags, você provavelmente também quer adicionar a bandeira --tag-name-filter catpara re-tag a nova commits correspondente como eles são reescritas, isto é, git filter-branch --index-filter 'git rm --cached --ignore-unmatch a b' --tag-name-filter cat HEAD(ver esta resposta relacionada )
naitsirhc
3
As instruções do Mac e outras informações aparecem na postagem original vinculada
nruth 5/18
3
git filter-branch --index-filter 'git rm --cached --ignore-unmatch <filename>' HEADworkorder direita da bat
eleijonmarck
minha resposta favorita. um ligeiro ajuste para uso em mac (usando comandos GNU)git rev-list --objects --all \ | git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' \ | awk '/^blob/ {print substr($0,6)}' \ | sort --numeric-sort --key=2 \ | gnumfmt --field=2 --to=iec-i --suffix=B --padding=7 --round=nearest
Florian Oswald
roteiro legal com a lista de rev, mas não funcionou para mim como um pseudônimo, alguma idéia de como fazer isso?
Robin Manoli
47

Depois de tentar praticamente todas as respostas no SO, finalmente encontrei essa jóia que removeu e excluiu rapidamente os arquivos grandes do meu repositório e me permitiu sincronizar novamente: http://www.zyxware.com/articles/4027/how-to-delete -files-permanentemente do seu local e repositórios git remotos

CD na sua pasta de trabalho local e execute o seguinte comando:

git filter-branch -f --index-filter "git rm -rf --cached --ignore-unmatch FOLDERNAME" -- --all

substitua FOLDERNAME pelo arquivo ou pasta que você deseja remover do repositório git fornecido.

Feito isso, execute os seguintes comandos para limpar o repositório local:

rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now

Agora empurre todas as alterações no repositório remoto:

git push --all --force

Isso limpará o repositório remoto.

Justin
fonte
Caiu como uma luva para mim.
Ramon Vasconcelos
3
Isso funcionou para mim também. Livra-se de uma pasta específica (no meu caso, uma que continha arquivos muito grandes ou um repositório do Github) no repositório, mas a mantém no sistema de arquivos local, caso exista.
Skizzo
Trabalhou para mim! sem história é deixado que é potencialmente confuso (se alguém onde clone agora), verifique se você tem um plano para atualizar quaisquer links quebrados, dependências, etc
Ruoho Ruotsi
38

Estes comandos funcionaram no meu caso:

git filter-branch --force --index-filter 'git rm --cached -r --ignore-unmatch oops.iso' --prune-empty --tag-name-filter cat -- --all
rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now

É um pouco diferente das versões acima.

Para aqueles que precisam enviar isso para o github / bitbucket (eu só testei isso com o bitbucket):

# WARNING!!!
# this will rewrite completely your bitbucket refs
# will delete all branches that you didn't have in your local

git push --all --prune --force

# Once you pushed, all your teammates need to clone repository again
# git pull will not work
Kostanos
fonte
4
Como é diferente do acima, por que é melhor?
Andy Hayden
1
Por alguma razão, a versão mkljun não reduz o espaço git no meu caso, eu já havia removido os arquivos do índice usando git rm --cached files. A proposta de Greg Bacon é mais completa, e é a mesma para esta mina, mas ele perdeu o índice --force para casos em que você usa o ramo de filtro por várias vezes, e ele escreveu tantas informações que minha versão é como resumo disso.
Kostanos
1
Isso realmente ajudou, mas eu precisava usar a -fopção de não apenas -rfaqui git rm --cached -rf --ignore-unmatch oops.isoem vez de git rm --cached -r --ignore-unmatch oops.isocomo por @ lfender6445 abaixo
drstevok
10

Apenas observe que esses comandos podem ser muito destrutivos. Se mais pessoas estiverem trabalhando no repositório, todas terão que puxar a nova árvore. Os três comandos do meio não são necessários se o seu objetivo NÃO é reduzir o tamanho. Como a ramificação do filtro cria um backup do arquivo removido e pode permanecer lá por um longo tempo.

$ git filter-branch --index-filter "git rm -rf --cached --ignore-unmatch YOURFILENAME" HEAD
$ rm -rf .git/refs/original/ 
$ git reflog expire --all 
$ git gc --aggressive --prune
$ git push origin master --force
mkljun
fonte
11
NÃO execute esses comandos a menos que queira criar uma dor imensa para si mesmo. Excluiu muitos dos meus arquivos de código fonte originais. Eu assumi que ele limparia alguns arquivos grandes do meu histórico de consolidação no GIT (conforme a pergunta original), no entanto, acho que esse comando foi projetado para limpar permanentemente arquivos da sua árvore de código fonte original (grande diferença!). Meu sistema: Windows, VS2012, Git Source Control Provider.
Contango 22/10/12
2
Eu usei esse comando: git filter-branch --force --index-filter 'git rm --cached -r --ignore-unmatch oops.iso' --prune-empty --tag-name-filter cat -- --allem vez de primeiro do seu código
Kostanos
8

Se você sabe que seu commit foi recente, em vez de passar pela árvore inteira, faça o seguinte: git filter-branch --tree-filter 'rm LARGE_FILE.zip' HEAD~10..HEAD

Soheil
fonte
7

Encontrei isso com uma conta bitbucket, onde havia armazenado acidentalmente backups gigantescos * .jpa do meu site.

git filter-branch --prune-empty --index-filter 'git rm -rf --cached --ignore-unmatch MY-BIG-DIRECTORY-OR-FILE' --tag-name-filter cat -- --all

Altere MY-BIG-DIRECTORYo espaço para a pasta em questão para reescrever completamente seu histórico ( incluindo tags ).

fonte: https://web.archive.org/web/20170727144429/http://naleid.com:80/blog/2012/01/17/finding-and-purging-big-files-from-git-history/

lfender6445
fonte
1
Essa resposta me ajudou, exceto que o script na resposta tem um pequeno problema e não pesquisa em todos os ramos que me formam. Mas o comando no link fez isso perfeitamente.
Ali B
5

Isso o removerá do seu histórico

git filter-branch --force --index-filter 'git rm -r --cached --ignore-unmatch bigfile.txt' --prune-empty --tag-name-filter cat -- --all
brilhar
fonte
Isso funcionou para mim obrigado !!
Sonja Brits
Isso funciona no meu caso. Eu corro isso no seu ramo mestre.
S. Domeng 20/02
4

Eu basicamente fiz o que estava nesta resposta: https://stackoverflow.com/a/11032521/1286423

(para histórico, copio e colo aqui)

$ git filter-branch --index-filter "git rm -rf --cached --ignore-unmatch YOURFILENAME" HEAD
$ rm -rf .git/refs/original/ 
$ git reflog expire --all 
$ git gc --aggressive --prune
$ git push origin master --force

Não deu certo, porque eu gosto de renomear e mudar muito as coisas. Então, algum arquivo grande estava em pastas que foram renomeadas, e acho que o gc não pôde excluir a referência a esses arquivos por causa da referência em treeobjetos que apontam para esse arquivo. Minha solução definitiva para realmente matá-lo foi:

# First, apply what's in the answer linked in the front
# and before doing the gc --prune --aggressive, do:

# Go back at the origin of the repository
git checkout -b newinit <sha1 of first commit>
# Create a parallel initial commit
git commit --amend
# go back on the master branch that has big file
# still referenced in history, even though 
# we thought we removed them.
git checkout master
# rebase on the newinit created earlier. By reapply patches,
# it will really forget about the references to hidden big files.
git rebase newinit

# Do the previous part (checkout + rebase) for each branch
# still connected to the original initial commit, 
# so we remove all the references.

# Remove the .git/logs folder, also containing references
# to commits that could make git gc not remove them.
rm -rf .git/logs/

# Then you can do a garbage collection,
# and the hidden files really will get gc'ed
git gc --prune --aggressive

Meu repositório (o .git) mudou de 32 MB para 388 KB, que nem mesmo a ramificação do filtro conseguiu limpar.

Dolanor
fonte
4

git filter-branché um comando poderoso que você pode usá-lo para excluir um arquivo enorme do histórico de confirmações. O arquivo permanecerá por um tempo e o Git o removerá na próxima coleta de lixo. Abaixo está o processo completo de exclusão de arquivos do histórico de confirmação . Por segurança, o processo abaixo executa os comandos em uma nova ramificação primeiro. Se o resultado for o que você precisava, redefina-o novamente para o ramo que você realmente deseja alterar.

# Do it in a new testing branch
$ git checkout -b test

# Remove file-name from every commit on the new branch
# --index-filter, rewrite index without checking out
# --cached, remove it from index but not include working tree
# --ignore-unmatch, ignore if files to be removed are absent in a commit
# HEAD, execute the specified command for each commit reached from HEAD by parent link
$ git filter-branch --index-filter 'git rm --cached --ignore-unmatch file-name' HEAD

# The output is OK, reset it to the prior branch master
$ git checkout master
$ git reset --soft test

# Remove test branch
$ git branch -d test

# Push it with force
$ git push --force origin master
zhangyu12
fonte
2

Use Git Extensions , é uma ferramenta de interface do usuário. Ele possui um plug-in chamado "Localizar arquivos grandes", que localiza arquivos lage nos repositórios e permite removê-los permanentemente.

Não use 'git filter-branch' antes de usar esta ferramenta, pois ela não poderá encontrar arquivos removidos por 'filter-branch' (Altough 'filter-branch' não remove completamente os arquivos dos arquivos do pacote de repositório) .

Nir
fonte
Este método é muito lento para repositórios grandes. Demorou mais de uma hora para listar os arquivos grandes. Então, quando vou excluir arquivos, depois de uma hora, é apenas 1/3 do processamento do primeiro arquivo que desejo excluir.
Kristianp #
Sim, é lento, mas funciona ... Você sabe de algo mais rápido?
Nir
1
Não o usei, mas o BFG Repo-Cleaner, conforme outra resposta nesta página.
22417 kristianp #
2

Você pode fazer isso usando o branch filtercomando:

git filter-branch --tree-filter 'rm -rf path/to/your/file' HEAD

John Foley
fonte
2

Existem respostas muito boas neste segmento, mas enquanto isso, muitas delas estão desatualizadas. O uso git-filter-branchnão é mais recomendado, porque é difícil de usar e muito lento em grandes repositórios.

git-filter-repo é muito mais rápido e mais simples de usar.

git-filter-repoé um script Python, disponível no github: https://github.com/newren/git-filter-repo .

Você precisa de apenas um arquivo: o script Python3 git-filter-repo. Copie-o para um caminho incluído na variável PATH. No Windows, pode ser necessário alterar a primeira linha do script (consulte INSTALL.md). Você precisa do Python3 instalado no seu sistema, mas isso não é grande coisa.

Primeiro você pode correr

git filter-repo --analyze

Isso ajuda a determinar o que fazer em seguida.

Você pode excluir seu arquivo ripado de DVD em qualquer lugar:

 git filter-repo --invert-paths --path-match DVD-rip

Repositório de filtros é realmente rápido. Uma tarefa que levou cerca de 9 horas no meu computador por ramo de filtro, foi concluída em 4 minutos pelo repositório de filtro. Você pode fazer muito mais coisas legais com o repositório de filtros. Consulte a documentação para isso.

Aviso: Faça isso em uma cópia do seu repositório. Muitas ações do repositório de filtro não podem ser desfeitas. O repositório de filtros alterará os hashes de confirmação de todos os commits modificados (é claro) e todos os seus descendentes até os últimos commits!

Donat
fonte
1

Quando você se deparar com esse problema, git rmnão será suficiente, pois o git lembra que o arquivo existia uma vez em nossa história e, portanto, manterá uma referência a ele.

Para piorar as coisas, também não é fácil refazer o processo, porque quaisquer referências ao blob impedirão que o coletor de lixo git limpe o espaço. Isso inclui referências remotas e referências de reflog.

Eu montei git forget-blob, um pequeno script que tenta remover todas essas referências e, em seguida, usa o git filter-branch para reescrever todos os commit no branch.

Depois que seu blob não for referenciado, git gcvocê se livrará dele

O uso é bem simples git forget-blob file-to-forget. Você pode obter mais informações aqui

https://ownyourbits.com/2017/01/18/completely-remove-a-file-from-a-git-repository-with-git-forget-blob/

Eu montei isso graças às respostas do Stack Overflow e a algumas entradas do blog. Créditos para eles!

nachoparker
fonte
você deve ter isso em homebrew
Cameron E
0

Além de git filter-branch(solução lenta, mas pura de git) e BFG (mais fácil e muito eficiente), também há outra ferramenta para filtrar com bom desempenho:

https://github.com/xoofx/git-rocket-filter

De sua descrição:

O objetivo do git-rocket-filter é semelhante ao comando git-filter-branch, fornecendo os seguintes recursos exclusivos:

  • Reescrita rápida de confirmações e árvores (na ordem de x10 a x100).
  • Suporte interno para listas brancas com --keep (mantém arquivos ou diretórios) e listas negras com opções --remove.
  • Uso de .gitignore como padrão para filtragem em árvore
  • Script C # rápido e fácil para filtragem de confirmação e filtragem em árvore
  • Suporte para script na filtragem em árvore por padrão de arquivo / diretório
  • Eliminar automaticamente a confirmação vazia / inalterada, incluindo as confirmações de mesclagem
Philippe
fonte