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.

user1116573
fonte
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
loganfsmyth
fonte
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.

apenas ninguém
fonte
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.

Timo
fonte
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).

Michael Durrant
fonte
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
Benjamin Wasula
fonte
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/

Rishabh Kumar
fonte
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!

shreya10
fonte