Почему мой репозиторий Git такой большой?
145M = .git/objects/pack/
Я написал скрипт, чтобы сложить размеры различий каждого коммита и коммита, прежде чем он вернется назад от вершины каждой ветви. Я получаю 129 МБ, что без сжатия и без учета одних и тех же файлов между филиалами и общей истории между филиалами.
Git принимает во внимание все эти вещи, поэтому я бы ожидал гораздо меньшего размера хранилища. Так почему.git такой большой?
Я сделал:
git fsck --full
git gc --prune=today --aggressive
git repack
Чтобы ответить о том, сколько файлов / коммитов, у меня есть 19 веток по 40 файлов в каждой. 287 коммитов, найденных с использованием:
git log --oneline --all|wc -l
Не следует брать десятки мегабайт для хранения информации об этом.
14 ответов
Я недавно вытащил неправильный удаленный репозиторий в локальный (git remote add ...
а также git remote update
). После удаления ненужных удаленных ссылок, веток и тегов у меня все еще оставалось 1,4 ГБ (!) Потерянного пространства в моем хранилище. Я смог избавиться от этого только путем клонирования git clone file:///path/to/repository
, Обратите внимание, что file://
имеет большое значение при клонировании локального репозитория - копируются только ссылочные объекты, а не вся структура каталогов.
Изменить: Вот один вкладыш Яна для воссоздания всех веток в новом репо:
d1=#original repo
d2=#new repo (must already exist)
cd $d1
for b in $(git branch | cut -c 3-)
do
git checkout $b
x=$(git rev-parse HEAD)
cd $d2
git checkout -b $b $x
cd $d1
done
Некоторые сценарии, которые я использую:
ГИТ-fatfiles
git rev-list --all --objects | \
sed -n $(git rev-list --objects --all | \
cut -f1 -d' ' | \
git cat-file --batch-check | \
grep blob | \
sort -n -k 3 | \
tail -n40 | \
while read hash type size; do
echo -n "-e s/$hash/$size/p ";
done) | \
sort -n -k1
...
89076 images/screenshots/properties.png
103472 images/screenshots/signals.png
9434202 video/parasite-intro.avi
Если вы хотите больше строк, см. Также версию Perl в соседнем ответе: /questions/31552274/pochemu-moj-repozitorij-git-takoj-bolshoj/31552286#31552286
мерзавец (для video/parasite.avi
):
git filter-branch -f --index-filter \
'git rm --force --cached --ignore-unmatch video/parasite-intro.avi' \
-- --all
rm -Rf .git/refs/original && \
git reflog expire --expire=now --all && \
git gc --aggressive && \
git prune
Примечание: второй скрипт предназначен для полного удаления информации из Git (включая всю информацию из reflogs). Используйте с осторожностью.
git gc
уже делает git repack
поэтому нет смысла переупаковывать вручную, если вы не собираетесь передавать ему какие-то специальные опции.
Первым шагом является проверка того, является ли большая часть пространства (как это обычно бывает) вашей объектной базой данных.
git count-objects -v
Это должно дать отчет о том, сколько распакованных объектов есть в вашем хранилище, сколько места они занимают, сколько у вас файлов пакета и сколько места они занимают.
В идеале, после перепаковки у вас не должно быть распакованных объектов и одного файла пакета, но вполне нормально, что некоторые объекты, на которые нет прямой ссылки в текущих ветвях, все еще присутствуют и распаковываются.
Если у вас есть одна большая упаковка, и вы хотите знать, что занимает место, вы можете перечислить объекты, которые составляют пакет, и то, как они хранятся.
git verify-pack -v .git/objects/pack/pack-*.idx
Обратите внимание, что verify-pack
берет файл индекса, а не сам файл пакета. Это дает отчет о каждом объекте в пакете, его истинном размере и его упакованном размере, а также информацию о том, был ли он "разграничен" и, если это так, происхождение дельта-цепи.
Чтобы увидеть, есть ли какие-либо необычно большие объекты в вашем хранилище, вы можете отсортировать результаты численно по третьему четвертому столбцу (например, | sort -k3n
).
Из этого вывода вы сможете увидеть содержимое любого объекта, используя git show
команда, хотя невозможно увидеть, где именно в истории фиксации хранилища ссылается на объект. Если вам нужно сделать это, попробуйте что-нибудь из этого вопроса.
Только к вашему сведению, главная причина, по которой вы можете получить нежелательные объекты, заключается в том, что git поддерживает reflog.
Рефлог предназначен для того, чтобы сохранить ваш зад, когда вы случайно удалили свою основную ветку или каким-то иным образом катастрофически повредили свой репозиторий.
Самый простой способ исправить это - обрезать ваши reflogs перед сжатием (просто убедитесь, что вы никогда не захотите возвращаться ни к одному из коммитов в reflog).
git gc --prune=now --aggressive
git repack
Это отличается от git gc --prune=today
в том, что он истекает весь reflog немедленно.
Если вы хотите узнать, какие файлы занимают место в вашем git-репозитории, запустите
git verify-pack -v .git/objects/pack/*.idx | sort -k 3 -n | tail -5
Затем извлеките ссылку на BLOB-объект, занимающий наибольшее количество места (последняя строка), и проверьте имя файла, занимающее так много места.
git rev-list --objects --all | grep <reference>
Это может быть даже файл, который вы удалили с git rm
, но git помнит об этом, потому что на него все еще есть ссылки, такие как теги, удаленные и reflog.
Как только вы знаете, от какого файла вы хотите избавиться, я рекомендую использовать git forget-blob
Это просто в использовании, просто сделай
git forget-blob file-to-forget
Это удалит все ссылки из git, удалит BLOB-объект из каждого коммита в истории и запустит сборку мусора, чтобы освободить место.
Скрипт git-fatfiles из ответа Ви прекрасен, если вы хотите увидеть размер всех ваших объектов, но он настолько медленный, что его невозможно использовать. Я снял ограничение на 40 строк, и он попытался использовать всю оперативную память моего компьютера вместо завершения. Поэтому я переписал это: это в тысячи раз быстрее, добавлены функции (необязательно), и была удалена какая-то странная ошибка - старая версия выдает неточные подсчеты, если вы суммируете выходные данные, чтобы увидеть общее пространство, используемое файлом.
#!/usr/bin/perl
use warnings;
use strict;
use IPC::Open2;
use v5.14;
# Try to get the "format_bytes" function:
my $canFormat = eval {
require Number::Bytes::Human;
Number::Bytes::Human->import('format_bytes');
1;
};
my $format_bytes;
if ($canFormat) {
$format_bytes = \&format_bytes;
}
else {
$format_bytes = sub { return shift; };
}
# parse arguments:
my ($directories, $sum);
{
my $arg = $ARGV[0] // "";
if ($arg eq "--sum" || $arg eq "-s") {
$sum = 1;
}
elsif ($arg eq "--directories" || $arg eq "-d") {
$directories = 1;
$sum = 1;
}
elsif ($arg) {
print "Usage: $0 [ --sum, -s | --directories, -d ]\n";
exit 1;
}
}
# the format is [hash, file]
my %revList = map { (split(' ', $_))[0 => 1]; } qx(git rev-list --all --objects);
my $pid = open2(my $childOut, my $childIn, "git cat-file --batch-check");
# The format is (hash => size)
my %hashSizes = map {
print $childIn $_ . "\n";
my @blobData = split(' ', <$childOut>);
if ($blobData[1] eq 'blob') {
# [hash, size]
$blobData[0] => $blobData[2];
}
else {
();
}
} keys %revList;
close($childIn);
waitpid($pid, 0);
# Need to filter because some aren't files--there are useless directories in this list.
# Format is name => size.
my %fileSizes =
map { exists($hashSizes{$_}) ? ($revList{$_} => $hashSizes{$_}) : () } keys %revList;
my @sortedSizes;
if ($sum) {
my %fileSizeSums;
if ($directories) {
while (my ($name, $size) = each %fileSizes) {
# strip off the trailing part of the filename:
$fileSizeSums{$name =~ s|/[^/]*$||r} += $size;
}
}
else {
while (my ($name, $size) = each %fileSizes) {
$fileSizeSums{$name} += $size;
}
}
@sortedSizes = map { [$_, $fileSizeSums{$_}] }
sort { $fileSizeSums{$a} <=> $fileSizeSums{$b} } keys %fileSizeSums;
}
else {
# Print the space taken by each file/blob, sorted by size
@sortedSizes = map { [$_, $fileSizes{$_}] }
sort { $fileSizes{$a} <=> $fileSizes{$b} } keys %fileSizes;
}
for my $fileSize (@sortedSizes) {
printf "%s\t%s\n", $format_bytes->($fileSize->[1]), $fileSize->[0];
}
Назовите этот git-fatfiles.pl и запустите его. Чтобы увидеть дисковое пространство, используемое всеми ревизиями файла, используйте --sum
вариант. Чтобы увидеть то же самое, но для файлов в каждом каталоге, используйте --directories
вариант. Если вы установите модуль cpan Number::Bytes::Human (запустите "cpan Number::Bytes::Human"), размеры будут отформатированы: "21M /path/to/file.mp4".
Вы уверены, что считаете только файлы.pack, а не.idx? Они находятся в том же каталоге, что и файлы.pack, но не содержат никаких данных репозитория (как указывает расширение, они являются не чем иным, как индексами для соответствующего пакета - фактически, если вы знаете правильную команду, вы можете легко воссоздать их из файла пакета, и сам git делает это при клонировании, поскольку только файл пакета передается с использованием собственного протокола git).
В качестве репрезентативного примера я взглянул на мой локальный клон репозитория linux-2.6:
$ du -c *.pack
505888 total
$ du -c *.idx
34300 total
Что указывает на увеличение примерно на 7%.
Есть также файлы снаружи objects/
; по моему личному опыту, из них index
а также gitk.cache
как правило, самые большие (всего 11M в моем клоне репозитория linux-2.6).
Другие объекты git, хранящиеся в .git
включать деревья, коммиты и теги. Коммиты и теги небольшие, но деревья могут стать большими, особенно если в вашем хранилище очень много маленьких файлов. Сколько файлов и сколько коммитов у вас есть?
Перед выполнением git filter-branch & git gc вы должны просмотреть теги, которые присутствуют в вашем репо. Любая реальная система, в которой есть автоматические теги для таких вещей, как непрерывная интеграция и развертывания, будет создавать необъявленные объекты, на которые все еще ссылаются эти теги, поэтому gc не может их удалить, и вы все равно будете удивляться, почему размер репо все еще так велик.
Лучший способ избавиться от всех ненужных вещей - запустить git-filter & git gc, а затем отправить master в новое голое хранилище. У нового голого репо будет зачищенное дерево.
Стоит проверить stacktrace.log. Это в основном журнал ошибок для отслеживания коммитов, которые потерпели неудачу. Недавно я обнаружил, что мой stacktrace.log - 65,5 ГБ, а мое приложение - 66,7 ГБ.
Это может произойти, если вы случайно добавили большой кусок файлов и поставили их на место, не обязательно фиксируя их. Это может произойти в rails
приложение при запуске bundle install --deployment
а потом случайно git add .
тогда вы увидите все файлы, добавленные в vendor/bundle
вы их удалили, но они уже попали в историю git, поэтому вы должны применить ответ Vi и изменить
video/parasite-intro.avi
от vendor/bundle
затем выполните вторую команду, которую он предоставляет.
Вы можете увидеть разницу с git count-objects -v
который в моем случае до применения скрипта имел размер пакета: 52 КБ, а после применения - 3,8 КБ.
Я создал новую реализацию сценария perl, который изначально был предоставлен в этом ответе (который с тех пор был переписан в ржавчине). После долгого изучения этого сценария perl я понял, что в нем есть несколько ошибок:
- Ошибки с путями с пробелами
- не сработало правильно (на самом деле не складывались все дельты)
-
--directory
не работал правильно (полагается) - Без него будет сообщаться размер эффективно-случайного объекта для данного пути, который, возможно, не был бы самым большим.
В итоге я полностью переписал сценарий. Он использует ту же последовательность команд git (
git rev-list
а также
git cat-file
), но затем он правильно обрабатывает данные, чтобы дать точные результаты. Я сохранил
--sum
а также
--directories
Особенности.
Я также изменил его, чтобы сообщать размер "диска" (то есть сжатый размер в репозитории git) файлов, а не исходные размеры файлов. Это кажется более актуальным для рассматриваемой проблемы. (Это можно сделать необязательным, если кому-то по какой-то причине нужны несжатые размеры.)
Я также добавил возможность сообщать только о файлах, которые были удалены, исходя из предположения, что все еще используемые файлы, вероятно, менее интересны. (То, как я это сделал, было чем-то вроде взлома; предложения приветствуются.)
Последний сценарий здесь . Я также могу скопировать его сюда, если это хороший этикет для StackOverflow? (Это ~180 строк.)
Создайте новую ветку, где текущая фиксация является начальной фиксацией со всей историей, чтобы уменьшить объекты git и размер истории.
Примечание. Прочтите комментарий перед запуском кода.
- git checkout --orphan latest_branch
- git add -A
- git commit -a -m «Начальное сообщение фиксации» # Сохранение изменений
- git branch -D master # Удаление основной ветки
- git branch -m master # переименование ветки как master
- git push -f origin master # нажимает на главную ветку
- git gc --aggressive --prune=all # удалить старые файлы