Remova o arquivo .pack grande criado por git


112

Eu verifiquei uma carga de arquivos em um branch e mesclei e, em seguida, tive que removê-los e agora fico com um grande arquivo .pack do qual não sei como me livrar.

Excluí todos os arquivos usando git rm -rf xxxxxxe também executei a --cachedopção.

Alguém pode me dizer como posso remover um grande arquivo .pack que está atualmente no seguinte diretório:

.git/objects/pack/pack-xxxxxxxxxxxxxxxxx.pack

Eu só preciso remover o branch que ainda tenho, mas não estou mais usando? Ou há algo mais que preciso executar?

Não tenho certeza de quanta diferença faz, mas mostra um cadeado contra o arquivo.

obrigado


EDITAR

Aqui estão alguns trechos do meu bash_history que devem dar uma ideia de como consegui entrar nesse estado (suponha que neste ponto eu estou trabalhando em um branch git chamado 'my-branch' e tenho uma pasta contendo mais pastas / arquivos):

git add .
git commit -m "Adding my branch changes to master"
git checkout master
git merge my-branch
git rm -rf unwanted_folder/
rm -rf unwanted_folder/     (not sure why I ran this as well but I did)

Eu pensei que também executei o seguinte, mas não aparece no bash_history com os outros:

git rm -rf --cached unwanted_folder/

Também pensei ter executado alguns comandos git (como git gc) para tentar organizar o arquivo do pacote, mas eles também não aparecem no arquivo .bash_history.


Você pode esclarecer como você os removeu? Se eles ainda estiverem no histórico de commits, então eles ainda estarão em seus arquivos de pacote.
— loganfsmyth de

Olá @loganfsmyth, adicionei os scripts de histórico do bash que espero ajudar.
— user1116573

Respostas:


201

O problema é que, embora você tenha removido os arquivos, eles ainda estão presentes nas revisões anteriores. Esse é o ponto principal do git, é que mesmo se você deletar algo, você ainda pode recuperá-lo acessando o histórico.

O que você está procurando fazer é chamado de reescrever a história, e envolveu o git filter-branch comando.

O GitHub tem uma boa explicação do problema em seu site. https://help.github.com/articles/remove-sensitive-data

Para responder à sua pergunta mais diretamente, o que você basicamente precisa executar é este comando unwanted_filename_or_foldersubstituído de acordo:

git filter-branch --index-filter 'git rm -r --cached --ignore-unmatch unwanted_filename_or_folder' --prune-empty

Isso removerá todas as referências aos arquivos do histórico ativo do repo.

Próxima etapa, para realizar um ciclo de GC para forçar todas as referências ao arquivo a serem expiradas e eliminadas do packfile. Nada precisa ser substituído nesses comandos.

git for-each-ref --format='delete %(refname)' refs/original | git update-ref --stdin
# or, for older git versions (e.g. 1.8.3.1) which don't support --stdin
# git update-ref $(git for-each-ref --format='delete %(refname)' refs/original)
git reflog expire --expire=now --all
git gc --aggressive --prune=now

3
Eu o marquei como aceito se isso tornar mais fácil para qualquer um que chegue a essa questão no futuro, embora eu realmente tenha resolvido meu problema na época criando um novo
— repositório

3
Eu não sei como você veio com isso, mas ... Você é o cara. Obrigado.
— Ezekiel Victor

5
Essa resposta me apontou na direção certa. Mas para realmente excluir os arquivos, mais 3 comandos são necessários 1) git for-each-ref --format='delete %(refname)' refs/original | git update-ref --stdin2) git reflog expire --expire=now --all3)git gc --prune=now
— arod

3
Acho que usar é bfgmuito mais fácil. Também é recomendado nos documentos oficiais do github: help.github.com/articles/…
— Timo

2
@Timo É bom adicionar uma nova resposta, se as coisas mudaram com o tempo. Vá em frente!
— loganfsmyth

12

Cenário A : Se seus arquivos grandes foram adicionados apenas a uma ramificação, você não precisa executar git filter-branch. Você só precisa excluir o branch e executar a coleta de lixo:

git branch -D mybranch
git reflog expire --expire-unreachable=all --all
git gc --prune=all

Cenário B : No entanto, com base no histórico do bash, parece que você mesclou as alterações no master. Se você não compartilhou as alterações com ninguém ( git pushainda não ). A coisa mais fácil seria restaurar o master para antes de mesclar com o branch que tinha os arquivos grandes. Isso eliminará todos os commits de seu branch e todos os commits feitos para o master após a fusão. Portanto, você pode perder alterações - além dos arquivos grandes - que você realmente queria:

git checkout master
git log # Find the commit hash just before the merge
git reset --hard <commit hash>

Em seguida, execute as etapas do cenário A.

Cenário C : Se houver outras mudanças no branch ou mudanças no master após a fusão que você deseja manter, seria melhor realocar o master e incluir seletivamente os commits que você deseja:

git checkout master
git log # Find the commit hash just before the merge
git rebase -i <commit hash>

Em seu editor, remova as linhas que correspondem aos commits que adicionaram os arquivos grandes, mas deixe todo o resto como está. Salve e saia. Seu branch master deve conter apenas o que você deseja, e nenhum arquivo grande. Observe que git rebasesem -pirá eliminar os commits de mesclagem, então você ficará com um histórico linear para o mestre depois <commit hash>. Isso provavelmente é bom para você, mas se não, você pode tentar com -p, mas git help rebasedizcombining -p with the -i option explicitly is generally not a good idea unless you know what you are doing .

Em seguida, execute os comandos do cenário A.


Há uma variante do Cenário A aqui com, entretanto, um problema inesperado extra.

Cenário Um problema de mina resolvido, para excluir uma grande quantidade de arquivo de pacote temporário. O repositório era gerenciado por um servidor de compilação e causa a criação de arquivos indesejados dentro da pasta .git / objects / pack. Eu poderia liberar GBs valiosos do meu disco.
— xrissz

7

Como loganfsmyth já declarou em sua resposta , você precisa limpar o histórico do git porque os arquivos continuam existindo lá mesmo após excluí-los do repo. Os documentos oficiais do GitHub recomendam o BFG, que considero mais fácil de usar do quefilter-branch :

Excluindo arquivos do histórico

Baixe o BFG de seu site. Certifique-se de ter o java instalado e, em seguida, crie um clone de espelho e limpe o histórico. Certifique-se de substituir YOUR_FILE_NAMEpelo nome do arquivo que deseja excluir:

git clone --mirror git://example.com/some-big-repo.git
java -jar bfg.jar --delete-files YOUR_FILE_NAME some-big-repo.git
cd some-big-repo.git
git reflog expire --expire=now --all && git gc --prune=now --aggressive
git push

Apagar uma pasta

O mesmo que acima, mas use --delete-folders

java -jar bfg.jar --delete-folders YOUR_FOLDER_NAME some-big-repo.git

Outras opções

O BFG também permite opções ainda mais sofisticadas (consulte a documentação ), como estas:

Remova todos os arquivos com mais de 100 MB do histórico:

java -jar bfg.jar --strip-blobs-bigger-than 100M some-big-repo.git

Importante!

Ao executar o BFG, tome cuidado para que ambos YOUR_FILE_NAMEe YOUR_FOLDER_NAMEsejam apenas nomes de arquivo / pasta. Eles não são caminhos , então algo como foo/bar.jpgnão funcionará! Em vez disso, todos os arquivos / pastas com o nome especificado serão removidos do histórico do repo, independentemente do caminho ou branch em que existam.


Eu me pergunto se desejo aplicar essa bfgferramenta a um repositório git local, como o comando deve ser?
— Angel Todorov

5

Uma opção:

execute git gcmanualmente para condensar vários arquivos de pacote em um ou alguns arquivos de pacote. Esta operação é persistente (ou seja, o arquivo de pacote grande manterá seu comportamento de compactação), portanto, pode ser benéfico compactar um repositório periodicamente comgit gc --aggressive

Outra opção é salvar o código e .git em algum lugar e, em seguida, excluir o .git e começar novamente usando este código existente, criando um novo repositório git ( git init).


Olá Michael, Tentei correr git gce consegui apenas alguns arquivos de pacote, mas o grande ainda é um deles e eu gostaria apenas de me livrar dele para que eu possa fazer backup da pasta externamente mais fácil (zip antes era 1 -2Mb, agora 55Mb). A menos que alguém possa sugerir algo mais, acho que devo criar um novo git. Presumo que isso signifique que vou perder o acesso aos branches que tenho atualmente, etc ...?
— user1116573

2
Desisti de tentar e apenas apaguei a pasta .git e criei um novo repositório git como você disse. Vou considerar isso uma lição aprendida. Obrigado Michael.
— user1116573

4
Isso não faz muito sentido. Por que você não pode simplesmente dizer ao git para consolidar o repositório atual e remover os arquivos do pacote no processo?
— jml

4

Execute o seguinte comando, substituindo PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATApelo caminho para o arquivo que deseja remover, não apenas pelo nome do arquivo. Esses argumentos irão:

  1. Força o Git a processar, mas não verificar, todo o histórico de cada branch e tag
  2. Remova o arquivo especificado, bem como quaisquer commits vazios gerados como resultado
  3. Substitua suas tags existentes
git filter-branch --force --index-filter "git rm --cached --ignore-unmatch PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA" --prune-empty --tag-name-filter cat -- --all

Isso removerá à força todas as referências aos arquivos do histórico ativo do repo.

Próxima etapa, para executar um ciclo de GC para forçar todas as referências ao arquivo a serem expiradas e eliminadas do arquivo de pacote. Nada precisa ser substituído nesses comandos.

git update-ref -d refs/original/refs/remotes/origin/master
git for-each-ref --format='delete %(refname)' refs/original | git update-ref --stdin
git reflog expire --expire=now --all
git gc --aggressive --prune=now

Finalmente, da 2ª parte, reduzi o repo de 28G para 158 milhões. Quase nada mais funcionou no Google. Obrigado.
— Sridhar Sarnobat

Eu segui os passos acima e empurrei como "git push origin --force --all" e ainda assim meus branches remotos (master, development e feature / ASD-1010) não limparam. Quando fiz uma nova clonagem do repositório remoto, os arquivos .pack ainda estavam presentes. Como posso refletir essa limpeza para todos os branches remotos do git ??
— Sambit Swain

1

Estou um pouco atrasado para o show mas caso a resposta acima não resolvesse a dúvida então encontrei outro jeito. Simplesmente remova o arquivo grande específico de .pack. Tive o problema de fazer check-in acidental de um arquivo grande de 2 GB. Segui as etapas explicadas neste link: http://www.ducea.com/2012/02/07/howto-completely-remove-a-file-from-git-history/


Depois de fazer este método, ele removerá completamente todo o histórico do projeto ou apenas removerá o arquivo especificado.
— Samim Aftab Ahmed

-3

esta é uma solução mais prática do que de codificação. Compacte o arquivo. Abra o zip no formato de exibição de arquivo (diferente de descompactar). Exclua o arquivo .pack. Descompacte e substitua a pasta. Funciona como um encanto!

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.