Двенадцать гигабайт. Именно столько весил этот репозиторий, когда я клонировал его в первый раз. Первая мысль — что-то сломалось у меня: кривой mount, разросшийся кэш, битый pack. Ничего подобного. Репозиторий действительно был таким. К концу недели я узнал куда больше, чем хотел, о том, как уменьшить Git-репозиторий и вычистить пароли из истории, не потеряв при этом ни строчки настоящей работы.
Дальше — как прошло это спасение и какие guardrails я оставил, чтобы всё не наросло обратно.
Археология
Прежде чем что-то чистить, надо понять, что именно ты чистишь. Репозиторий не дорастает до двенадцати гигабайт случайно — за каждым мегабайтом стоит история, обычно нехорошая.
Я прошёлся по базе объектов через git rev-list --objects --all и git cat-file, отсортировав блобы по размеру. Карта сокровищ нарисовалась сама. Прямо в историю были закоммичены:
- JAR-ы и ZIP-ы — сборочные артефакты, которым рядом с Git делать нечего
- Docker-образы, закоммиченные тарболами, потому что кому-то они понадобились «всего один раз»
- Тысячи мёртвых веток, большинство — заброшены годами
- И то, от чего меня передёрнуло: настоящие пароли и API-ключи, лежащие в старых коммитах и тихо ждущие того, кто окажется достаточно любопытным, чтобы запустить
git log -p
Последний пункт меняет картину целиком. Это был не раздутый репозиторий. Это был инцидент безопасности, который просто ещё не заметили.
Сначала ротация, потом чистка
Все первым делом хватаются за BFG Repo Cleaner или git filter-repo, чтобы вырезать секреты из истории. Инструмент правильный — но порядок действий важнее инструмента.
Переписывание истории не «отменяет» утечку. Как только пароль попал в remote, который тянули люди, CI-раннеры и форки, — считай, что он скомпрометирован. Поэтому последовательность не обсуждается:
- Сначала ротировать все засвеченные креды — новые ключи, новые пароли, старые отозвать
- Потом вычистить старые значения из каждого коммита
- И только потом force-push и re-clone у всех
Сделаешь наоборот — потратишь выходные на переписывание истории ради ключа, который всё ещё рабочий.
Чистка
Секреты ротированы — началась операция. BFG Repo Cleaner вырезал из всей истории каждый бинарник и каждую известную секретную строку. Большие файлы, у которых всё же была причина существовать, переехали в нормальный artifact registry — туда, где бинарникам и место. Мёртвые ветки — в архив и под нож пачкой. Затем git gc --aggressive и repack, чтобы реально вернуть место на диске.
Двенадцать гигабайт превратились в аккуратный репозиторий, который клонируется за секунды, а не за перекур.
Раз уж залез — пайплайн
У настолько запущенного репозитория и пайплайн никогда не бывает здоровым. Раз всё и так вскрыто, я заодно перекроил CI:
- Последовательные стадии разнесены в параллель там, где никто не заставлял их идти по очереди
- Добавлено Docker layer caching — перестали пересобирать мир на каждый коммит
- Выборочное тестирование: правка одного модуля гоняет тесты этого модуля, а не всего проекта
Время клона и сборки упало с «пойду сварю кофе» до «уже готово».
Guardrails
Вот часть, которую пропускают в большинстве чисток, — и единственная, что реально важна вдолгую. Чистка без guardrails — временная заплатка: те же привычки, что раздули репозиторий, за год наполнят его снова. Поэтому я поставил забор:
- pre-commit hooks, которые отбивают любой файл больше 5 МБ ещё до попадания в историю
- Политика жизненного цикла веток: авто-удаление через 7 дней после мержа
- Secret scanning прямо в CI-пайплайне — засвеченный ключ роняет билд, а не оседает в истории
- Мониторинг размера репозитория с алертами, чтобы разрастание ловилось на 200 МБ, а не на 12 ГБ
Вывод
Ваш Git-репозиторий — фундамент всего delivery-пайплайна. Всё — CI, деплои, онбординг разработчиков, безопасность — стоит поверх него. Когда он чистый, всё ниже по течению течёт свободно. Когда это двенадцатигигабайтная капсула времени с плохими решениями — всё ниже по течению вязнет.
Если клон занимает больше времени, чем чашка кофе, репозиторий пытается вам что-то сказать. Прислушайтесь — а потом сегодня же прогоните по нему secret scan. Результат может не понравиться. Именно поэтому и стоит посмотреть.