Git: Очистка репозитория от ненужных больших файлов

December 12, 2014

Бывали ли у вас ситуации, когда случайно сами или кто-то из ваших коллег добавлял в git большие файлы, например бекап сайта или дамп sql. Удалить мы его конечно удалим, но информация о нем все равно будет храниться в гите и таскаться / загружаться каждый новый git clone.

Для начала, потребовалось установить, что же это был за файл, собираем мусор в бд:

git gc

Counting objects: 457, done.
Delta compression using 2 threads.
Compressing objects: 100% (457/457), done.
Writing objects: 100% (457/457), done.
Total 457 (delta 250), reused 0 (delta 0)

Чтобы быстро узнать, сколько у нас занято места, можно воспользоваться командой count-objects:

git count-objects -v

count: 20
size: 11
in-pack: 4411
packs: 1
size-pack: 2716918
prune-packable: 0
garbage: 0

Запись size-pack — это размер упакованных файлов в килобайтах.

Найдём файл, который занимает много места. При вызове git gc все объекты упаковываются в один файл, но, несмотря на это, определить самые крупные файлы можно, запустив служебную команду git verify-pack, и отсортировав её вывод по третьей колонке, в которой записан размер файла. К тому же, так как нас интересуют только самые крупные файлы, оставим только последние несколько строк, направив вывод команде tail:

git verify-pack -v .git/objects/pack/pack-#HASH_CODE#.pack | sort -k 3 -n | tail -3

e3f094f522629ae358806b17daf78246c27c007b blob 1486 734 4667
05408d195263d853f09dca71d55116663690c27c blob 12908 3478 1189
7a9eb2fba2b1811321254ac360970fc169ba2330 blob 2056716 2056872 5401

Большой объект в самом внизу. Для того чтобы узнать, что это за файл, воспользуемся командой rev-list. Если передать ей ключ --objects, то она выдаст хеши всех коммитов, а также хеши объектов и соответствующие им имена файлов. Воспользуемся этим для определения имени выбранного объекта:

git rev-list --objects --all | grep #HASH_CODE#

7a9eb2fba2b1811321254ac360970fc169ba2330 git.tbz2

Теперь необходимо удалить данный файл из всех деревьев в прошлом по истории. Легко получить все коммиты, которые меняли данный файл:

git log --pretty=oneline --branches -- git.tbz2

da3f30d019005479c99eb4c3406225613985a1db oops - removed large tarball
6df764092f3e7c8f5f94cbe08ee5cf42e92a0289 added git tarball

Необходимо переписать все коммиты, начиная с 6df76 для полного удаления данного файла. Для этого воспользуемся командой filter-branch:

git filter-branch --index-filter 'git rm --cached --ignore-unmatch git.tbz2'

Rewrite 6df764092f3e7c8f5f94cbe08ee5cf42e92a0289 (1/2)rm 'git.tbz2'
Rewrite da3f30d019005479c99eb4c3406225613985a1db (2/2)
Ref 'refs/heads/master' was rewritten

Теперь наша история не содержит ссылок на данный файл. Однако, в reflog и в новом наборе ссылок, добавленном Git’ом в .git/refs/original после выполнения filter-branch, ссылки на него всё ещё присутствуют. Поэтому необходимо их удалить, а потом переупаковать базу. Необходимо избавиться от всех возможных ссылок на старые коммиты перед переупаковкой:

rm -rf .git/refs/original
rm -rf .git/logs/

Делаем все не привязанные объекты истекшими по времени

git reflog expire --all --expire='0 days'

Проверяем на наличие не привязанных объектов

git fsck --full --unreachable

Производит переупаковку с удалением не привязанных объектов

git repack -A -d

И удаляем эти объекты

git prune

Комментарии

comments powered by Disqus