2013-04-20

Липосакция репозитория

Сборка HelloWorld на Maven

Предыстория

До того, как мы переехали на maven, мы жили в счастливом неведении: проект собирал нам eclipse, бинарные зависимости хранились рядом и коммитились в репозиторий. Двух книг оказалось достаточно, чтобы понять как низко мы пали. После этого мир изменился и уже никогда не будет прежним, а непортируемая сборка, которую нельзя запустить из консоли без IDE, будет восприниматься как нечто, о чем лучше забыть и не вспоминать.

Мы успешно выпилили все старые артефакты и всё бы ничего, но наш репозиторий перевалил за гиг и клонирование занимало какое-то совсем неприличное время. Посему, между возможностью собрать любую старую версию (на тот момент мы еще не стабилизировали версии) и маленьким репозиторием, мы выбрали последний вариант. Похерив все хэши, мы удалили все бинарники сократив размер репозитория в 7 раз до 150 мегабайт. Все стало просто летать.

Хирургическое вмешательство

1 Этап. Определение кандидатов на отстрел

Такие файлы должны удовлетворять двум простым правилам:
  • это должен быть бинарный файл
  • он должен быть удален на момент поиска
Бинарный файл можно опредлить по расширению и отсутствию изменений в этом файле.

2. Этап. Составление расстрельного списка

Используем стиль для вывода списка файлов:

#Для списка измененных
#changeset = "{file_mods}"
#Для списка удаленных
changeset = "{file_dels}"

file_mod  = "{file_mod}\n"
file_del  = "{file_del}\n"

Дальше составляем два списка командами hg log --style style_del | sort -u и hg log --style style_mod | sort -u. В первом приближении нам нужно из списка удаленных исключить все элменты которые встречаются в списке измененных. Вычесть файлы можно несколькими способами. Дальше вычитаем при помощи пайпов:
comm -2 -3 \
<(hg log --style ~/path/to/style_del | sort -u) \
<(hg log --style ~/path/to/style_mod | sort -u).
Для экспериментов можно передавать диапазон ревизий -r 0:100 чтобы оптимизировать время.

Полученный список нужно будет просмотреть глазами и составить список расширений для удаления. Список расширений можно посмотреть прогнав через | grep -oE "\.\w*$" | sort -u. В нашем случае он получился таким: deb, gz, jar, mp3, so, wav.

Прогоняем через sed, выводя только файлы, совпавшие по расширению, формируя filemap для hg convert. Итоговая строка фильтрации выглядит так:
comm -2 -3 \
<(hg log --style ~/path/to/style_del | sort -u) \

<(hg log --style ~/path/to/style_mod | sort -u) \
| sed -n 's/\(.*\)\(deb\|gz\|jar\|mp3\|so\|wav\)/exclude "&"/p' \
> filemap

3 Этап. Зачистка

Получившийся filemap скармливаем в hg convert --filemap ~/path/to/filemap ~/repo ~/repo_clean. На выходе получаем переродившийся репозиторий без шлака и мусора.

Дисклеймер

Здесь описана операция, успешно проведенная нами год назад. За это вресмя ни разу не понадобилось доступиться к удаленным файлам. На данный момент при 5к коммитах, репозиторий занимает 500Мб. При этом часть бинарных ресурсов всё еще жива и используется.

Фильтрация идет в основном средствами линуксовых команд. Хотя mercurial предоставляет прекрасный API для доступа к репозиториям, которым сам же и пользуется, на котором можно было бы написать понятную фильтрацию без подобных майндфаков: /\(.*\)\( и приседания с ключами команд, на тот момент с этим API я был незнаком, а сейчас просто воспроизвожу свои записки для сохранения в назидание потомкам.